Merge "Snap to destination page before animating folder close." into sc-dev
diff --git a/Android.bp b/Android.bp
index 002f6fe..2c9d664 100644
--- a/Android.bp
+++ b/Android.bp
@@ -44,6 +44,7 @@
"src/com/android/launcher3/ResourceUtils.java",
"src/com/android/launcher3/testing/TestProtocol.java",
],
+ resource_dirs: [ ],
manifest: "tests/tapl/AndroidManifest.xml",
platform_apis: true,
}
@@ -94,27 +95,35 @@
min_sdk_version: "28",
}
+// Library with all the dependencies for building Launcher3
+android_library {
+ name: "Launcher3ResLib",
+ srcs: [ ],
+ resource_dirs: ["res"],
+ static_libs: [
+ "LauncherPluginLib",
+ "launcher_quickstep_log_protos_lite",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.recyclerview_recyclerview",
+ "androidx.dynamicanimation_dynamicanimation",
+ "androidx.fragment_fragment",
+ "androidx.preference_preference",
+ "androidx.slice_slice-view",
+ "androidx.cardview_cardview",
+ "iconloader_base",
+ ],
+ manifest: "AndroidManifest-common.xml",
+ sdk_version: "current",
+ min_sdk_version: "26",
+}
+
//
// Build rule for Launcher3 dependencies lib.
//
android_library {
name: "Launcher3CommonDepsLib",
- static_libs: [
- "androidx.recyclerview_recyclerview",
- "androidx.dynamicanimation_dynamicanimation",
- "androidx.preference_preference",
- "androidx.slice_slice-view",
- "iconloader_base",
- "LauncherPluginLib",
- "launcher_quickstep_log_protos_lite"
- ],
- srcs: [
- "src_build_config/**/*.java",
- ],
- resource_dirs: ["res"],
- optimize: {
- enabled: false,
- },
+ srcs: ["src_build_config/**/*.java"],
+ static_libs: ["Launcher3ResLib"],
sdk_version: "current",
min_sdk_version: "26",
manifest: "AndroidManifest-common.xml",
@@ -164,22 +173,42 @@
],
}
-//
-// Launcher Robolectric test target.
-//
-java_library {
- name: "Launcher3TestCommon",
- libs: [
- "Launcher3CommonDepsLib",
+// Library with all the dependencies for building quickstep
+android_library {
+ name: "QuickstepResLib",
+ srcs: [ ],
+ resource_dirs: [
+ "quickstep/res",
+ "quickstep/overview_ui_overrides/res",
],
+ static_libs: [
+ "Launcher3ResLib",
+ "SystemUISharedLib",
+ "SystemUI-statsd",
+ ],
+ manifest: "quickstep/AndroidManifest.xml",
+ min_sdk_version: "28",
+}
+
+
+// Source code used for test helpers
+filegroup {
+ name: "launcher-src-ext-tests",
+ srcs: ["ext_tests/src/**/*.java"],
+}
+
+// Common source files used to build launcher
+filegroup {
+ name: "launcher-src-no-build-config",
srcs: [
"src/**/*.java",
"src_shortcuts_overrides/**/*.java",
- "src_ui_overrides/**/*.java",
- "ext_tests/src/**/*.java",
- "tests/src_common/**/*.java",
+ "quickstep/src/**/*.java",
],
- target_sdk_version: "29",
- sdk_version: "current",
- min_sdk_version: "26",
+}
+
+// Proguard files for Launcher3
+filegroup {
+ name: "launcher-proguard-rules",
+ srcs: ["proguard.flags"],
}
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 4fd2e40..4e72260 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -116,8 +116,7 @@
android:theme="@style/AppItemActivityTheme"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
- android:exported="true"
- android:label="@string/action_add_to_workspace" >
+ android:exported="true">
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
<action android:name="android.content.pm.action.CONFIRM_PIN_APPWIDGET" />
diff --git a/SharedLibWrapper/build.gradle b/SharedLibWrapper/build.gradle
deleted file mode 100644
index 674e38a..0000000
--- a/SharedLibWrapper/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-apply plugin: 'java'
-
-final String ANDROID_TOP = "${rootDir}/../../.."
-final String FRAMEWORK_PREBUILTS_DIR = "${ANDROID_TOP}/prebuilts/framework_intermediates/"
-
-sourceSets {
- main {
- java.srcDirs = ["${ANDROID_TOP}/frameworks/lib/systemui/SharedLibWrapper/src"]
- }
-}
-
-sourceCompatibility = 1.8
-
-dependencies {
- implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
- compileOnly fileTree(dir: "$ANDROID_TOP/prebuilts/fullsdk-${org.gradle.internal.os.OperatingSystem.current().isMacOsX() ? "darwin" : "linux"}/platforms/${COMPILE_SDK}", include: 'android.jar')
-}
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index f36439d..2671604 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -24,6 +24,8 @@
<uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
diff --git a/go/OWNERS b/go/OWNERS
new file mode 100644
index 0000000..903b3c4
--- /dev/null
+++ b/go/OWNERS
@@ -0,0 +1,2 @@
+rajekumar@google.com
+spivack@google.com
diff --git a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index b438da3..e6af848 100644
--- a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -17,14 +17,14 @@
<com.android.quickstep.views.GoOverviewActionsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/overview_actions_height"
+ android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom">
<LinearLayout
android:id="@+id/action_buttons"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_height="@dimen/overview_actions_height"
+ android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal">
<Space
diff --git a/go/quickstep/res/values/config.xml b/go/quickstep/res/values/config.xml
index 9dca137..a21381c 100644
--- a/go/quickstep/res/values/config.xml
+++ b/go/quickstep/res/values/config.xml
@@ -20,5 +20,5 @@
<string name="niu_actions_package" translatable="false"/>
<!-- Feature Flags -->
- <bool name="enable_niu_actions">false</bool>
+ <bool name="enable_niu_actions">true</bool>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/strings.xml b/go/quickstep/res/values/strings.xml
index 71e2f3a..61c8cd9 100644
--- a/go/quickstep/res/values/strings.xml
+++ b/go/quickstep/res/values/strings.xml
@@ -10,5 +10,5 @@
<!-- Label for a button that translates a screenshot of the current app. [CHAR_LIMIT=40] -->
<string name="action_translate">Translate</string>
<!-- Label for a button that triggers Search on a screenshot of the current app. [CHAR_LIMIT=40] -->
- <string name="action_search">Search</string>
+ <string name="action_search">Lens</string>
</resources>
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index b102a39..350e0d1 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -20,13 +20,17 @@
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint;
+import android.app.assist.AssistContent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
import android.os.SystemClock;
import android.text.TextUtils;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.R;
+import com.android.quickstep.util.AssistContentRequester;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.systemui.shared.recents.model.Task;
@@ -40,6 +44,8 @@
public static final String ACTION_TRANSLATE = "com.android.quickstep.ACTION_TRANSLATE";
public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH";
public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos";
+ public static final String ACTIONS_URL = "niu_actions_app_url";
+ private static final String TAG = "TaskOverlayFactoryGo";
// Empty constructor required for ResourceBasedOverride
public TaskOverlayFactoryGo(Context context) {}
@@ -56,8 +62,8 @@
* @param <T> The type of View in which the overlay will be placed
*/
public static final class TaskOverlayGo<T extends OverviewActionsView> extends TaskOverlay {
-
- private String mPackageName;
+ private String mNIUPackageName;
+ private String mWebUrl;
private TaskOverlayGo(TaskThumbnailView taskThumbnailView) {
super(taskThumbnailView);
@@ -70,29 +76,57 @@
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
boolean rotated) {
getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
- mPackageName =
+ mNIUPackageName =
mApplicationContext.getResources().getString(R.string.niu_actions_package);
- if (thumbnail == null || TextUtils.isEmpty(mPackageName)) {
+ if (thumbnail == null || TextUtils.isEmpty(mNIUPackageName)) {
return;
}
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
- boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
+
+ int taskId = task.key.id;
+ AssistContentRequester contentRequester =
+ new AssistContentRequester(mApplicationContext);
+ contentRequester.requestAssistContent(taskId, this::onAssistContentReceived);
}
- private void sendNIUIntent(String actionType) {
+ /** Provide Assist Content to the overlay. */
+ @VisibleForTesting
+ public void onAssistContentReceived(AssistContent assistContent) {
+ mWebUrl = assistContent.getWebUri() != null
+ ? assistContent.getWebUri().toString() : null;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ mWebUrl = null;
+ }
+
+ /**
+ * Creates and sends an Intent corresponding to the button that was clicked
+ */
+ @VisibleForTesting
+ public void sendNIUIntent(String actionType) {
Intent intent = createNIUIntent(actionType);
mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
}
private Intent createNIUIntent(String actionType) {
- return new Intent(actionType)
+ Intent intent = new Intent(actionType)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
- .setPackage(mPackageName)
+ .setPackage(mNIUPackageName)
.putExtra(ELAPSED_NANOS, SystemClock.elapsedRealtimeNanos());
+
+ if (mWebUrl != null) {
+ intent.putExtra(ACTIONS_URL, mWebUrl);
+ }
+
+ return intent;
}
protected class OverlayUICallbacksGoImpl extends OverlayUICallbacksImpl
@@ -128,6 +162,11 @@
}
}
}
+
+ @VisibleForTesting
+ public void setImageActionsAPI(ImageActionsApi imageActionsApi) {
+ mImageApi = imageActionsApi;
+ }
}
/**
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
new file mode 100644
index 0000000..38c9919
--- /dev/null
+++ b/quickstep/Android.bp
@@ -0,0 +1,28 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_apps_Launcher3_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["packages_apps_Launcher3_license"],
+}
+
+filegroup {
+ name: "launcher3-quickstep-robolectric-src",
+ path: "robolectric_tests",
+ srcs: ["robolectric_tests/src/**/*.java"],
+}
diff --git a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index d15a2d2..1da25e7 100644
--- a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -16,14 +16,14 @@
-->
<com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/overview_actions_height"
+ android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom">
<LinearLayout
android:id="@+id/action_buttons"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_height="@dimen/overview_actions_height"
+ android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal">
<Space
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 34ff91d..c61610a 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -22,5 +22,4 @@
android:layout_height="wrap_content"
android:text="@string/recents_clear_all"
android:textColor="?attr/workspaceTextColor"
- android:textSize="14sp"
- android:translationY="@dimen/task_thumbnail_half_top_margin" />
\ No newline at end of file
+ android:textSize="14sp" />
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 0f9a6aa..7e5b85c 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -13,6 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- NOTE! don't add dimensions for margins / paddings / sizes that change per orientation to this
+ file, they need to be loaded at runtime. -->
<com.android.quickstep.views.TaskView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -24,8 +26,7 @@
<com.android.quickstep.views.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
+ android:layout_height="match_parent"/>
<com.android.quickstep.views.IconView
android:id="@+id/icon"
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
index 6e1aa1e..87649f7 100644
--- a/quickstep/res/layout/taskbar_divider.xml
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -18,6 +18,4 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/taskbar_divider_thickness"
android:layout_height="@dimen/taskbar_divider_height"
- android:layout_marginStart="@dimen/taskbar_icon_spacing"
- android:layout_marginEnd="@dimen/taskbar_icon_spacing"
android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_view.xml b/quickstep/res/layout/taskbar_view.xml
new file mode 100644
index 0000000..34a88ea
--- /dev/null
+++ b/quickstep/res/layout/taskbar_view.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher3.taskbar.TaskbarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/taskbar_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/taskbar_size"
+ android:background="@android:color/transparent"
+ android:layout_gravity="bottom"
+ android:gravity="center"
+ android:visibility="gone" />
+
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index c03eaa2..7cb01f6 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -16,4 +16,6 @@
-->
<resources>
<dimen name="task_card_menu_horizontal_padding">24dp</dimen>
+
+ <dimen name="overview_task_margin">8dp</dimen>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 755bce8..3036341 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -15,11 +15,8 @@
-->
<resources>
-
- <dimen name="task_thumbnail_top_margin">80dp</dimen>
- <dimen name="task_thumbnail_half_top_margin">40dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
- <dimen name="task_icon_top_margin">16dp</dimen>
+ <dimen name="task_thumbnail_icon_size_grid">32dp</dimen>
<!-- For screens without rounded corners -->
<dimen name="task_corner_radius_small">2dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 0764bb3..1603321 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -99,62 +99,61 @@
<!-- Dismiss button string for search education view -->
<string name="search_edu_dismiss">Got it.</string>
-
- <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
- <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
- <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] -->
- <string name="back_gesture_tutorial_engaged_subtitle_swipe_inward_right_edge" translatable="false">Start at the right edge and swipe toward the middle</string>
- <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
- <string name="back_gesture_feedback_swipe_too_far_from_right_edge" translatable="false">Make sure you swipe from the far right edge</string>
- <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is cancelled. [CHAR LIMIT=100] -->
- <string name="back_gesture_feedback_cancelled_right_edge" translatable="false">Make sure you swipe straight to the left and let go</string>
-
- <!-- Title shown during interactive part of Back gesture tutorial for left edge. [CHAR LIMIT=30] -->
- <string name="back_gesture_tutorial_playground_title_swipe_inward_left_edge" translatable="false">Try the other side</string>
- <!-- Subtitle shown during interactive parts of Back gesture tutorial for left edge. [CHAR LIMIT=60] -->
- <string name="back_gesture_tutorial_engaged_subtitle_swipe_inward_left_edge" translatable="false">That\'s it! Now try swiping from the left edge.</string>
<!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
- <string name="back_gesture_feedback_swipe_too_far_from_left_edge" translatable="false">Make sure you swipe from the far left edge</string>
+ <string name="back_gesture_feedback_swipe_too_far_from_left_edge">Make sure you swipe from the far-left edge.</string>
<!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is cancelled. [CHAR LIMIT=100] -->
- <string name="back_gesture_feedback_cancelled_left_edge" translatable="false">Make sure you swipe straight to the right and let go</string>
-
+ <string name="back_gesture_feedback_cancelled_left_edge">Make sure you swipe from the left edge to the middle of the screen and let go.</string>
+ <!-- Feedback shown after completing the left back gesture before continuing on to the right edge. [CHAR LIMIT=60] -->
+ <string name="back_gesture_feedback_complete_left_edge">That\'s it! Now try swiping from the right edge.</string>
+ <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
+ <string name="back_gesture_feedback_swipe_too_far_from_right_edge">Make sure you swipe from the far-right edge.</string>
+ <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is cancelled. [CHAR LIMIT=100] -->
+ <string name="back_gesture_feedback_cancelled_right_edge">Make sure you swipe from the right edge to the middle of the screen and let go.</string>
+ <!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is cancelled. [CHAR LIMIT=100] -->
+ <string name="back_gesture_feedback_complete">You completed the go back gesture. Next up, learn how to go Home.</string>
<!-- Feedback shown during interactive parts of Back gesture tutorial when the gesture is within the nav bar region. [CHAR LIMIT=100] -->
- <string name="back_gesture_feedback_swipe_in_nav_bar" translatable="false">Make sure you don\'t swipe too close to the bottom of the screen</string>
+ <string name="back_gesture_feedback_swipe_in_nav_bar">Make sure you don\'t swipe too close to the bottom of the screen.</string>
<!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] -->
- <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string>
-
- <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] -->
- <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string>
- <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] -->
- <string name="home_gesture_tutorial_playground_subtitle" translatable="false">Try swiping upward from the bottom edge of the screen</string>
+ <string name="back_gesture_tutorial_confirm_subtitle">To change the sensitivity of the back gesture, go to Settings</string>
<!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
- <string name="home_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string>
- <!-- Feedback shown during interactive parts of Home gesture tutorial when the Overview gesture is detected. [CHAR LIMIT=100] -->
- <string name="home_gesture_feedback_overview_detected" translatable="false">Make sure you don\'t pause before letting go</string>
- <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
- <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string>
+ <!-- Introduction title for the Back gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="back_gesture_intro_title">Swipe to go back</string>
+ <!-- Introduction subtitle for the Back gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="back_gesture_intro_subtitle">To go back to the last screen, swipe from the left or right edge to the middle of the screen.</string>
- <!-- Title shown during interactive part of Overview gesture tutorial. [CHAR LIMIT=30] -->
- <string name="overview_gesture_tutorial_playground_title" translatable="false">Tutorial: Switch Apps</string>
- <!-- Subtitle shown during interactive parts of Overview gesture tutorial. [CHAR LIMIT=60] -->
- <string name="overview_gesture_tutorial_playground_subtitle" translatable="false">Swipe up from the bottom of the screen and hold</string>
+ <string name="home_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
+ <!-- Feedback shown during interactive parts of Home gesture tutorial when the Overview gesture is detected. [CHAR LIMIT=100] -->
+ <string name="home_gesture_feedback_overview_detected">Make sure you don\'t pause before letting go.</string>
+ <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
+ <string name="home_gesture_feedback_wrong_swipe_direction">Make sure you swipe straight up.</string>
+ <string name="home_gesture_feedback_complete">You completed the go Home gesture. Next up, learn how to switch apps.</string>
+ <!-- Introduction title for the Home gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="home_gesture_intro_title">Swipe to go home</string>
+ <!-- Introduction subtitle for the Home gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="home_gesture_intro_subtitle">Swipe up from the bottom of your screen. This gesture always takes you to the Home screen.</string>
+
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
- <string name="overview_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string>
+ <string name="overview_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. [CHAR LIMIT=100] -->
- <string name="overview_gesture_feedback_home_detected" translatable="false">Try holding the window for longer before releasing</string>
+ <string name="overview_gesture_feedback_home_detected">Try holding the window for longer before releasing.</string>
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
- <string name="overview_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up and pause</string>
+ <string name="overview_gesture_feedback_wrong_swipe_direction">Make sure you swipe straight up, then pause.</string>
+ <string name="overview_gesture_feedback_complete">You completed the switch apps gesture. You\'re ready to use your phone!</string>
+ <!-- Introduction title for the Overview gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="overview_gesture_intro_title">Swipe to switch apps</string>
+ <!-- Introduction subtitle for the Overview gesture tutorial. [CHAR LIMIT=100] -->
+ <string name="overview_gesture_intro_subtitle">Swipe up from the bottom of your screen, hold, then release.</string>
<!-- Title shown during interactive part of Assistant gesture tutorial. [CHAR LIMIT=30] -->
<string name="assistant_gesture_tutorial_playground_title" translatable="false">Tutorial: Assistant</string>
<!-- Subtitle shown during interactive parts of Assistant gesture tutorial. [CHAR LIMIT=60] -->
<string name="assistant_gesture_tutorial_playground_subtitle" translatable="false">Try swiping diagonally from a bottom corner of the screen</string>
<!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture is started too far from the corner. [CHAR LIMIT=100] -->
- <string name="assistant_gesture_feedback_swipe_too_far_from_corner" translatable="false">Make sure you swipe from a bottom corner of the screen</string>
+ <string name="assistant_gesture_feedback_swipe_too_far_from_corner" translatable="false">Make sure you swipe from a bottom corner of the screen.</string>
<!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture doesn't go diagonally enough. [CHAR LIMIT=100] -->
- <string name="assistant_gesture_feedback_swipe_not_diagonal" translatable="false">Make sure you swipe diagonally</string>
+ <string name="assistant_gesture_feedback_swipe_not_diagonal" translatable="false">Make sure you swipe diagonally.</string>
<!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture doesn't go far enough. [CHAR LIMIT=100] -->
- <string name="assistant_gesture_feedback_swipe_not_long_enough" translatable="false">Try swiping further</string>
+ <string name="assistant_gesture_feedback_swipe_not_long_enough" translatable="false">Try swiping further.</string>
<!-- Title shown in sandbox mode part of gesture tutorial. [CHAR LIMIT=30] -->
<string name="sandbox_mode_title" translatable="false">Sandbox Mode</string>
@@ -172,11 +171,19 @@
<string name="sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the left/right edge of the screen</string>
<!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
- <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
- <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
- <string name="gesture_tutorial_action_button_label_done" translatable="false">Done</string>
+ <string name="gesture_tutorial_confirm_title">All set</string>
+ <!-- Button text shown on a button on the feedback popup to proceed to the next tutorial step. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_next">Next</string>
+ <!-- Button text shown on a button on the feedback popup to complete the tutorial. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_done">Done</string>
<!-- Button text shown on a button to go to Settings. [CHAR LIMIT=14] -->
- <string name="gesture_tutorial_action_button_label_settings" translatable="false">Settings</string>
+ <string name="gesture_tutorial_action_button_label_settings">Settings</string>
+ <!-- Feedback title to try again. [CHAR LIMIT=30] -->
+ <string name="gesture_tutorial_try_again">Try again</string>
+ <!-- Feedback title for a successful gesture. [CHAR LIMIT=30] -->
+ <string name="gesture_tutorial_nice">Nice!</string>
+ <!-- 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>
<!-- ******* Overview ******* -->
<!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
@@ -185,4 +192,14 @@
<string name="action_screenshot">Screenshot</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
<string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
+
+ <!-- ******* Skip tutorial dialog ******* -->
+ <!-- Title for the dialog that allows the user to skip the gesture navigation tutorial. [CHAR_LIMIT=40] -->
+ <string name="skip_tutorial_dialog_title">Skip navigation tutorial?</string>
+ <!-- Subtitle for the dialog that allows the user to skip the gesture navigation tutorial. [CHAR_LIMIT=40] -->
+ <string name="skip_tutorial_dialog_subtitle">You can find this later in the Tips app</string>
+ <!-- Button text shown on a button on the tutorial skip dialog to return to the tutorial. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_cancel">Cancel</string>
+ <!-- Button text shown on a button on the tutorial skip dialog to exit the tutorial. [CHAR LIMIT=14] -->
+ <string name="gesture_tutorial_action_button_label_skip">Skip</string>
</resources>
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index 7049af0..9df9ab1 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -37,6 +37,7 @@
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
+@org.junit.Ignore
public class RecentsActivityTest {
@Test
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 688f323..88079ae 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -17,6 +17,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -29,6 +31,7 @@
import com.android.launcher3.shadows.LShadowDisplay;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.LauncherActivityInterface;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import org.hamcrest.Description;
@@ -162,7 +165,7 @@
@Override
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, null, this);
+ proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
return new SurfaceParams[] {builder.build()};
}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 20a645e..085db6d 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -46,6 +46,7 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.taskbar.TaskbarStateHandler;
+import com.android.launcher3.taskbar.TaskbarView;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.DisplayController;
@@ -95,9 +96,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mAppTransitionManager = new QuickstepTransitionManager(this);
- mAppTransitionManager.registerRemoteAnimations();
-
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
addMultiWindowModeChangedListener(mDepthController);
}
@@ -225,6 +223,9 @@
overviewPanel.init(mActionsView, splitPlaceholderView);
mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
+ mAppTransitionManager = new QuickstepTransitionManager(this);
+ mAppTransitionManager.registerRemoteAnimations();
+
addTaskbarIfNecessary();
addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
}
@@ -243,9 +244,10 @@
mTaskbarController = null;
}
if (mDeviceProfile.isTaskbarPresent) {
+ TaskbarView taskbarViewOnHome = (TaskbarView) mHotseat.getTaskbarView();
TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this);
mTaskbarController = new TaskbarController(this,
- taskbarActivityContext.getTaskbarContainerView());
+ taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
mTaskbarController.init();
}
}
@@ -334,12 +336,16 @@
}
@Override
+ public float getNormalTaskbarScale() {
+ if (mTaskbarController != null) {
+ return mTaskbarController.getTaskbarScaleOnHome();
+ }
+ return super.getNormalTaskbarScale();
+ }
+
+ @Override
public void onDragLayerHierarchyChanged() {
onLauncherStateOrFocusChanged();
-
- if (mTaskbarController != null) {
- mTaskbarController.onLauncherDragLayerHierarchyChanged();
- }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 343b87e..82a83fc 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -27,7 +27,6 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
@@ -67,9 +66,9 @@
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.util.ActivityOptionsWrapper;
@@ -86,7 +85,6 @@
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -98,6 +96,7 @@
import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.LinkedHashMap;
@@ -148,9 +147,6 @@
public static final int CONTENT_ALPHA_DURATION = 217;
protected static final int CONTENT_TRANSLATION_DURATION = 350;
- // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
- public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
-
private static final int MAX_NUM_TASKS = 5;
protected final BaseQuickstepLauncher mLauncher;
@@ -314,9 +310,11 @@
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
- Rect windowTargetBounds = getWindowTargetBounds(appTargets);
+ final int rotationChange = getRotationChange(appTargets);
+ // Note: the targetBounds are relative to the launcher
+ Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
- areAllTargetsTranslucent(appTargets)));
+ areAllTargetsTranslucent(appTargets), rotationChange));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */,
@@ -345,19 +343,29 @@
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
- private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] appTargets) {
- Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
- if (mLauncher.isInMultiWindowMode()) {
- for (RemoteAnimationTargetCompat target : appTargets) {
- if (target.mode == MODE_OPENING) {
- bounds.set(target.screenSpaceBounds);
- if (target.localBounds != null) {
- bounds.set(target.localBounds);
- } else {
- bounds.offsetTo(target.position.x, target.position.y);
- }
- return bounds;
- }
+ private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
+ int rotationChange) {
+ RemoteAnimationTargetCompat target = null;
+ for (RemoteAnimationTargetCompat t : appTargets) {
+ if (t.mode != MODE_OPENING) continue;
+ target = t;
+ break;
+ }
+ if (target == null) return new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+ final Rect bounds = new Rect(target.screenSpaceBounds);
+ if (target.localBounds != null) {
+ bounds.set(target.localBounds);
+ } else {
+ bounds.offsetTo(target.position.x, target.position.y);
+ }
+ if (rotationChange != 0) {
+ if ((rotationChange % 2) == 1) {
+ // undoing rotation, so our "original" parent size is actually flipped
+ Utilities.rotateBounds(bounds, mDeviceProfile.heightPx, mDeviceProfile.widthPx,
+ 4 - rotationChange);
+ } else {
+ Utilities.rotateBounds(bounds, mDeviceProfile.widthPx, mDeviceProfile.heightPx,
+ 4 - rotationChange);
}
}
return bounds;
@@ -429,9 +437,6 @@
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
};
} else if (mLauncher.isInState(OVERVIEW)) {
- AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
- allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
} else {
mDragLayerAlpha.setValue(alphas[0]);
@@ -510,7 +515,7 @@
private Animator getOpeningWindowAnimators(View v,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
- Rect windowTargetBounds, boolean appTargetsAreTranslucent) {
+ Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
RectF launcherIconBounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
!appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
@@ -610,6 +615,10 @@
final int windowCropWidth = crop.width();
final int windowCropHeight = crop.height();
+ if (rotationChange != 0) {
+ Utilities.rotateBounds(crop, mDeviceProfile.widthPx,
+ mDeviceProfile.heightPx, rotationChange);
+ }
// Scale the size of the icon to match the size of the window crop.
float scaleX = iconWidth / windowCropWidth;
@@ -649,7 +658,20 @@
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
- matrix.postTranslate(windowTransX0, windowTransY0);
+ if (rotationChange == 1) {
+ matrix.postTranslate(windowTransY0,
+ mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));
+ } else if (rotationChange == 2) {
+ matrix.postTranslate(
+ mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),
+ mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));
+ } else if (rotationChange == 3) {
+ matrix.postTranslate(
+ mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),
+ windowTransX0);
+ } else {
+ matrix.postTranslate(windowTransX0, windowTransY0);
+ }
floatingView.update(floatingIconBounds, mIconAlpha.value, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
@@ -658,17 +680,25 @@
.withAlpha(1f - mIconAlpha.value)
.withCornerRadius(mWindowRadius.value)
.withShadowRadius(mShadowRadius.value);
- } else {
+ } else if (target.mode == MODE_CLOSING) {
if (target.localBounds != null) {
final Rect localBounds = target.localBounds;
tmpPos.set(target.localBounds.left, target.localBounds.top);
} else {
tmpPos.set(target.position.x, target.position.y);
}
-
- matrix.setTranslate(tmpPos.x, tmpPos.y);
final Rect crop = new Rect(target.screenSpaceBounds);
crop.offsetTo(0, 0);
+
+ if ((rotationChange % 2) == 1) {
+ int tmp = crop.right;
+ crop.right = crop.bottom;
+ crop.bottom = tmp;
+ tmp = tmpPos.x;
+ tmpPos.x = tmpPos.y;
+ tmpPos.y = tmp;
+ }
+ matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
.withWindowCrop(crop)
.withAlpha(1f);
@@ -825,14 +855,26 @@
return unlockAnimator;
}
+ private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
+ int rotationChange = 0;
+ for (RemoteAnimationTargetCompat target : appTargets) {
+ if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
+ rotationChange = target.rotationChange;
+ }
+ }
+ return rotationChange;
+ }
+
/**
* Animator that controls the transformations of the windows the targets that are closing.
*/
private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
+ final int rotationChange = getRotationChange(appTargets);
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
Matrix matrix = new Matrix();
Point tmpPos = new Point();
+ Rect tmpRect = new Rect();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode
@@ -859,26 +901,32 @@
tmpPos.set(target.position.x, target.position.y);
}
+ final Rect crop = new Rect(target.screenSpaceBounds);
+ crop.offsetTo(0, 0);
if (target.mode == MODE_CLOSING) {
+ tmpRect.set(target.screenSpaceBounds);
+ if ((rotationChange % 2) != 0) {
+ final int right = crop.right;
+ crop.right = crop.bottom;
+ crop.bottom = right;
+ }
matrix.setScale(mScale.value, mScale.value,
- target.screenSpaceBounds.centerX(),
- target.screenSpaceBounds.centerY());
+ tmpRect.centerX(),
+ tmpRect.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
+ .withWindowCrop(crop)
.withAlpha(mAlpha.value)
.withCornerRadius(windowCornerRadius)
.withShadowRadius(mShadowRadius.value);
- } else {
+ } else if (target.mode == MODE_OPENING) {
matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
+ .withWindowCrop(crop)
.withAlpha(1f);
}
- final Rect crop = new Rect(target.screenSpaceBounds);
- crop.offsetTo(0, 0);
- params[i] = builder
- .withWindowCrop(crop)
- .build();
+ params[i] = builder.build();
}
surfaceApplier.scheduleApply(params);
}
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 66b1a86..d17a5ae 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -16,7 +16,6 @@
package com.android.launcher3.appprediction;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.ALL_APPS;
import android.annotation.TargetApi;
@@ -30,7 +29,6 @@
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.Interpolator;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
@@ -41,7 +39,6 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.Themes;
@@ -287,13 +284,6 @@
}
@Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
- PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
- // Don't use setViewAlpha as we want to control the visibility ourselves.
- setter.setFloat(this, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, allAppsFade);
- }
-
- @Override
public void setVerticalScroll(int scroll, boolean isScrolledOut) {
setTranslationY(scroll);
mIsScrolledOut = isScrolledOut;
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 83234e3..8e92b59 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,20 +16,14 @@
package com.android.launcher3.appprediction;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.IntProperty;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@@ -44,7 +38,6 @@
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
@@ -53,8 +46,6 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.util.Themes;
-import com.android.quickstep.AnimatedFloat;
import java.util.ArrayList;
import java.util.List;
@@ -63,22 +54,6 @@
public class PredictionRowView extends LinearLayout implements
OnDeviceProfileChangeListener, FloatingHeaderRow {
- private static final IntProperty<PredictionRowView> TEXT_ALPHA =
- new IntProperty<PredictionRowView>("textAlpha") {
- @Override
- public void setValue(PredictionRowView view, int alpha) {
- view.setTextAlpha(alpha);
- }
-
- @Override
- public Integer get(PredictionRowView view) {
- return view.mIconLastSetTextAlpha;
- }
- };
-
- private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
- (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
-
private final Launcher mLauncher;
private int mNumPredictedAppsPerRow;
@@ -88,21 +63,9 @@
// The set of predicted apps resolved from the component names and the current set of apps
private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
- private final int mIconTextColor;
- private final int mIconFullTextAlpha;
- private int mIconLastSetTextAlpha;
- // Might use mIconFullTextAlpha instead of mIconLastSetTextAlpha if we are translucent.
- private int mIconCurrentTextAlpha;
-
private FloatingHeaderView mParent;
private boolean mScrolledOut;
- private float mScrollTranslation = 0;
- private final AnimatedFloat mContentAlphaFactor =
- new AnimatedFloat(this::updateTranslationAndAlpha);
- private final AnimatedFloat mOverviewScrollFactor =
- new AnimatedFloat(this::updateTranslationAndAlpha);
-
private boolean mPredictionsEnabled = false;
@Nullable
@@ -117,15 +80,9 @@
setOrientation(LinearLayout.HORIZONTAL);
mFocusHelper = new SimpleFocusIndicatorHelper(this);
-
mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
mLauncher = Launcher.getLauncher(context);
mLauncher.addOnDeviceProfileChangeListener(this);
-
- mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
- mIconFullTextAlpha = Color.alpha(mIconTextColor);
- mIconCurrentTextAlpha = mIconFullTextAlpha;
-
updateVisibility();
}
@@ -246,7 +203,6 @@
}
int predictionCount = mPredictedApps.size();
- int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
for (int i = 0; i < getChildCount(); i++) {
BubbleTextView icon = (BubbleTextView) getChildAt(i);
@@ -254,7 +210,6 @@
if (predictionCount > i) {
icon.setVisibility(View.VISIBLE);
icon.applyFromWorkspaceItem(mPredictedApps.get(i));
- icon.setTextColor(iconColor);
} else {
icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
}
@@ -269,27 +224,6 @@
mParent.onHeightUpdated();
}
- public void setTextAlpha(int textAlpha) {
- mIconLastSetTextAlpha = textAlpha;
- if (getAlpha() < 1 && textAlpha > 0) {
- // If the entire header is translucent, make sure the text is at full opacity so it's
- // not double-translucent. However, we support keeping the text invisible (alpha == 0).
- textAlpha = mIconFullTextAlpha;
- }
- mIconCurrentTextAlpha = textAlpha;
- int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
- for (int i = 0; i < getChildCount(); i++) {
- ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
- }
- }
-
- @Override
- public void setAlpha(float alpha) {
- super.setAlpha(alpha);
- // Reapply text alpha so that we update it to be full alpha if the row is now translucent.
- setTextAlpha(mIconLastSetTextAlpha);
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
@@ -299,34 +233,11 @@
@Override
public void setVerticalScroll(int scroll, boolean isScrolledOut) {
mScrolledOut = isScrolledOut;
- updateTranslationAndAlpha();
if (!isScrolledOut) {
- mScrollTranslation = scroll;
- updateTranslationAndAlpha();
+ setTranslationY(scroll);
}
- }
-
- private void updateTranslationAndAlpha() {
- if (mPredictionsEnabled) {
- setTranslationY((1 - mOverviewScrollFactor.value) * mScrollTranslation);
-
- float factor = ALPHA_FACTOR_INTERPOLATOR.getInterpolation(mOverviewScrollFactor.value);
- float endAlpha = factor + (1 - factor) * (mScrolledOut ? 0 : 1);
- setAlpha(mContentAlphaFactor.value * endAlpha);
- AlphaUpdateListener.updateVisibility(this);
- }
- }
-
- @Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
- PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
- // Text follows all apps visibility
- int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0;
- setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade);
- setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
- (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR);
- setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
- headerFade);
+ setAlpha(mScrolledOut ? 0 : 1);
+ AlphaUpdateListener.updateVisibility(this);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 9944270..eed493d 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -42,6 +42,7 @@
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -62,9 +63,11 @@
import com.android.launcher3.logging.StatsLogManager.EventEnum;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
import java.util.Locale;
+import java.util.Optional;
import java.util.function.ObjIntConsumer;
import java.util.function.Predicate;
@@ -174,6 +177,7 @@
return null;
}
ComponentName cn = null;
+ ShortcutInfo shortcutInfo = null;
String id = null;
switch (info.getItemCase()) {
@@ -188,6 +192,14 @@
LauncherAtom.Shortcut si = info.getShortcut();
if (!TextUtils.isEmpty(si.getShortcutId())
&& (cn = parseNullable(si.getShortcutName())) != null) {
+ Optional<ShortcutInfo> opt = new ShortcutRequest(mContext,
+ userHandle).forPackage(cn.getPackageName(), si.getShortcutId()).query(
+ ShortcutRequest.ALL).stream().findFirst();
+ if (opt.isPresent()) {
+ shortcutInfo = opt.get();
+ } else {
+ return null;
+ }
id = "shortcut:" + si.getShortcutId();
}
break;
@@ -210,6 +222,9 @@
return createTempFolderTarget();
}
if (id != null && cn != null) {
+ if (shortcutInfo != null) {
+ return new AppTarget.Builder(new AppTargetId(id), shortcutInfo).build();
+ }
return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
.setClassName(cn.getClassName())
.build();
@@ -217,6 +232,7 @@
return null;
}
+
private AppTarget createTempFolderTarget() {
return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()),
mContext.getPackageName(), Process.myUserHandle())
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index b1b4d70..df3657d 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -73,6 +73,7 @@
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
+ private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
private static final boolean IS_DEBUG = false;
private static final String TAG = "QuickstepModelDelegate";
@@ -217,7 +218,7 @@
registerWidgetsPredictor(apm.createAppPredictionSession(
new AppPredictionContext.Builder(context)
.setUiSurface("widgets")
- .setPredictedTargetCount(mIDP.numColumns)
+ .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
.build()));
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index ce94305..1f268cc 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -48,10 +48,6 @@
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) {
- if (config.onlyPlayAtomicComponent()) {
- return;
- }
-
if (SysUINavigationMode.getMode(mLauncher) != TWO_BUTTONS) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index fe8f0c6..249ef3a 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -193,7 +193,6 @@
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation animation) {
if (mSurface == null
- || config.onlyPlayAtomicComponent()
|| config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
|| mIgnoreStateChangesDuringMultiWindowAnimation) {
return;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8312b82..5513c16 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -35,14 +35,13 @@
private final DeviceProfile mDeviceProfile;
private final LayoutInflater mLayoutInflater;
private final TaskbarContainerView mTaskbarContainerView;
- private final float mIconScale;
public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
super(launcher);
mDeviceProfile = launcher.getDeviceProfile().copy(this);
float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
- mIconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
- mDeviceProfile.updateIconSize(mIconScale, getResources());
+ float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
+ mDeviceProfile.updateIconSize(iconScale, getResources());
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
@@ -54,10 +53,7 @@
return mTaskbarContainerView;
}
- /**
- * @return A LayoutInflater to use in this Context. Views inflated with this LayoutInflater will
- * be able to access this TaskbarActivityContext via ActivityContext.lookupContext().
- */
+ @Override
public LayoutInflater getLayoutInflater() {
return mLayoutInflater;
}
@@ -76,11 +72,4 @@
public Rect getFolderBoundingBox() {
return mTaskbarContainerView.getFolderBoundingBox();
}
-
- /**
- * @return The ratio of taskbar icon size vs normal workspace/hotseat icon size.
- */
- public float getTaskbarIconScale() {
- return mIconScale;
- }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
similarity index 72%
rename from quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
rename to quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
index 715e4ca..7c54e2d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -20,33 +20,38 @@
import android.animation.Animator;
import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Utilities;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
/**
- * Works with TaskbarController to update the TaskbarView's alpha based on LauncherState, whether
- * Launcher is in the foreground, etc.
+ * Works with TaskbarController to update the TaskbarView's visual properties based on factors such
+ * as LauncherState, whether Launcher is in the foreground, etc.
*/
-public class TaskbarVisibilityController {
+public class TaskbarAnimationController {
private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
private final BaseQuickstepLauncher mLauncher;
- private final TaskbarController.TaskbarVisibilityControllerCallbacks mTaskbarCallbacks;
+ private final TaskbarController.TaskbarAnimationControllerCallbacks mTaskbarCallbacks;
// Background alpha.
- private AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
+ private final AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
this::onTaskbarBackgroundAlphaChanged);
// Overall visibility.
- private AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
+ private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
this::updateVisibilityAlpha);
- private AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
+ private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
this::updateVisibilityAlpha);
- public TaskbarVisibilityController(BaseQuickstepLauncher launcher,
- TaskbarController.TaskbarVisibilityControllerCallbacks taskbarCallbacks) {
+ // Scale.
+ private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
+ this::updateScale);
+
+ public TaskbarAnimationController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarAnimationControllerCallbacks taskbarCallbacks) {
mLauncher = launcher;
mTaskbarCallbacks = taskbarCallbacks;
}
@@ -72,6 +77,10 @@
return mTaskbarVisibilityAlphaForLauncherState;
}
+ protected AnimatedFloat getTaskbarScaleForLauncherState() {
+ return mTaskbarScaleForLauncherState;
+ }
+
protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
.setDuration(duration);
@@ -85,6 +94,7 @@
private void onTaskbarBackgroundAlphaChanged() {
mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
updateVisibilityAlpha();
+ updateScale();
}
private void updateVisibilityAlpha() {
@@ -101,6 +111,15 @@
setNavBarButtonAlpha(1f - taskbarAlpha);
}
+ 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
+ // LauncherState if Launcher is paused.
+ float scale = mTaskbarScaleForLauncherState.value;
+ scale = Utilities.mapRange(mTaskbarBackgroundAlpha.value, scale, 1f);
+ mTaskbarCallbacks.updateTaskbarScale(scale);
+ }
+
private void setNavBarButtonAlpha(float navBarAlpha) {
SystemUiProxy.INSTANCE.get(mLauncher).setNavBarButtonAlpha(navBarAlpha, false);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
index 528f43e..5202d91 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -84,7 +84,7 @@
private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
return insetsInfo -> {
if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
- || mTaskbarView.isDraggingItem()) {
+ || mTaskbarView.getVisibility() != VISIBLE || mTaskbarView.isDraggingItem()) {
// We're invisible or dragging out of taskbar, let touches pass through us.
insetsInfo.touchableRegion.setEmpty();
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 744339b..de23ad2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -19,10 +19,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REPLACE_TASKBAR_WITH_HOTSEAT;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
@@ -42,7 +38,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Hotseat;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
@@ -53,6 +49,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -69,13 +66,14 @@
private static final String WINDOW_TITLE = "Taskbar";
private final TaskbarContainerView mTaskbarContainerView;
- private final TaskbarView mTaskbarView;
+ private final TaskbarView mTaskbarViewInApp;
+ private final TaskbarView mTaskbarViewOnHome;
private final BaseQuickstepLauncher mLauncher;
private final WindowManager mWindowManager;
// Layout width and height of the Taskbar in the default state.
private final Point mTaskbarSize;
private final TaskbarStateHandler mTaskbarStateHandler;
- private final TaskbarVisibilityController mTaskbarVisibilityController;
+ private final TaskbarAnimationController mTaskbarAnimationController;
private final TaskbarHotseatController mHotseatController;
private final TaskbarRecentsController mRecentsController;
private final TaskbarDragController mDragController;
@@ -90,19 +88,22 @@
private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher;
+ private boolean mIsAnimatingToApp;
public TaskbarController(BaseQuickstepLauncher launcher,
- TaskbarContainerView taskbarContainerView) {
+ TaskbarContainerView taskbarContainerView, TaskbarView taskbarViewOnHome) {
mLauncher = launcher;
mTaskbarContainerView = taskbarContainerView;
mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
- mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
- mTaskbarView.construct(createTaskbarViewCallbacks());
+ mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
+ mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
+ mTaskbarViewOnHome = taskbarViewOnHome;
+ mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
mWindowManager = mLauncher.getWindowManager();
mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
- mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher,
- createTaskbarVisibilityControllerCallbacks());
+ mTaskbarAnimationController = new TaskbarAnimationController(mLauncher,
+ createTaskbarAnimationControllerCallbacks());
mHotseatController = new TaskbarHotseatController(mLauncher,
createTaskbarHotseatControllerCallbacks());
mRecentsController = new TaskbarRecentsController(mLauncher,
@@ -110,16 +111,23 @@
mDragController = new TaskbarDragController(mLauncher);
}
- private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() {
- return new TaskbarVisibilityControllerCallbacks() {
+ private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
+ return new TaskbarAnimationControllerCallbacks() {
@Override
public void updateTaskbarBackgroundAlpha(float alpha) {
- mTaskbarView.setBackgroundAlpha(alpha);
+ mTaskbarViewInApp.setBackgroundAlpha(alpha);
}
@Override
public void updateTaskbarVisibilityAlpha(float alpha) {
mTaskbarContainerView.setAlpha(alpha);
+ mTaskbarViewOnHome.setAlpha(alpha);
+ }
+
+ @Override
+ public void updateTaskbarScale(float scale) {
+ mTaskbarViewInApp.setScaleX(scale);
+ mTaskbarViewInApp.setScaleY(scale);
}
};
}
@@ -147,13 +155,22 @@
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
ActivityOptions.makeBasic());
} else if (tag instanceof FolderInfo) {
- if (mLauncher.hasBeenResumed()) {
- FolderInfo folderInfo = (FolderInfo) tag;
- onClickedOnFolderFromHome(folderInfo);
- } else {
- FolderIcon folderIcon = (FolderIcon) view;
- onClickedOnFolderInApp(folderIcon);
- }
+ FolderIcon folderIcon = (FolderIcon) view;
+ Folder folder = folderIcon.getFolder();
+
+ setTaskbarWindowFullscreen(true);
+
+ mTaskbarContainerView.post(() -> {
+ folder.animateOpen();
+
+ folder.iterateOverItems((itemInfo, itemView) -> {
+ itemView.setOnClickListener(getItemOnClickListener());
+ itemView.setOnLongClickListener(getItemOnLongClickListener());
+ // To play haptic when dragging, like other Taskbar items do.
+ itemView.setHapticFeedbackEnabled(true);
+ return false;
+ });
+ });
} else {
ItemClickHandler.INSTANCE.onClick(view);
}
@@ -163,52 +180,30 @@
};
}
- // Open the real folder in Launcher.
- private void onClickedOnFolderFromHome(FolderInfo folderInfo) {
- alignRealHotseatWithTaskbar();
-
- FolderIcon folderIcon = (FolderIcon) mLauncher.getHotseat()
- .getFirstItemMatch((info, v) -> info == folderInfo);
- folderIcon.post(folderIcon::performClick);
- }
-
- // Open the Taskbar folder, and handle clicks on folder items.
- private void onClickedOnFolderInApp(FolderIcon folderIcon) {
- Folder folder = folderIcon.getFolder();
-
- setTaskbarWindowFullscreen(true);
-
- mTaskbarContainerView.post(() -> {
- folder.animateOpen();
-
- folder.iterateOverItems((itemInfo, itemView) -> {
- itemView.setOnClickListener(getItemOnClickListener());
- itemView.setOnLongClickListener(getItemOnLongClickListener());
- // To play haptic when dragging, like other Taskbar items do.
- itemView.setHapticFeedbackEnabled(true);
- return false;
- });
- });
- }
-
@Override
public View.OnLongClickListener getItemOnLongClickListener() {
- return view -> {
- if (mLauncher.hasBeenResumed() && view.getTag() instanceof ItemInfo) {
- alignRealHotseatWithTaskbar();
- return mDragController.startWorkspaceDragOnLongClick(view);
- } else {
- return mDragController.startSystemDragOnLongClick(view);
- }
- };
+ return mDragController::startSystemDragOnLongClick;
}
@Override
- public int getEmptyHotseatViewVisibility() {
+ public int getEmptyHotseatViewVisibility(TaskbarView taskbarView) {
// When on the home screen, we want the empty hotseat views to take up their full
// space so that the others line up with the home screen hotseat.
- return mLauncher.hasBeenResumed() || mIsAnimatingToLauncher
- ? View.INVISIBLE : View.GONE;
+ boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
+ || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
+ return isOnHomeScreen ? View.INVISIBLE : View.GONE;
+ }
+
+ @Override
+ public float getNonIconScale(TaskbarView taskbarView) {
+ return taskbarView == mTaskbarViewOnHome ? getTaskbarScaleOnHome() : 1f;
+ }
+
+ @Override
+ public void onItemPositionsChanged(TaskbarView taskbarView) {
+ if (taskbarView == mTaskbarViewOnHome) {
+ alignRealHotseatWithTaskbar();
+ }
}
};
}
@@ -217,7 +212,7 @@
return new TaskbarHotseatControllerCallbacks() {
@Override
public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
- mTaskbarView.updateHotseatItems(hotseatItemInfos);
+ mTaskbarViewInApp.updateHotseatItems(hotseatItemInfos);
mLatestLoadedHotseatItems = hotseatItemInfos;
dedupeAndUpdateRecentItems();
}
@@ -234,7 +229,8 @@
@Override
public void updateRecentTaskAtIndex(int taskIndex, Task task) {
- mTaskbarView.updateRecentTaskAtIndex(taskIndex, task);
+ mTaskbarViewInApp.updateRecentTaskAtIndex(taskIndex, task);
+ mTaskbarViewOnHome.updateRecentTaskAtIndex(taskIndex, task);
}
};
}
@@ -243,23 +239,30 @@
* Initializes the Taskbar, including adding it to the screen.
*/
public void init() {
- mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
+ mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(),
mRecentsController.getNumRecentIcons());
- mTaskbarContainerView.init(mTaskbarView);
+ mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(),
+ mRecentsController.getNumRecentIcons());
+ mTaskbarContainerView.init(mTaskbarViewInApp);
addToWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
- mTaskbarVisibilityController.init();
+ mTaskbarAnimationController.init();
mHotseatController.init();
mRecentsController.init();
- SCALE_PROPERTY.set(mTaskbarView, mLauncher.hasBeenResumed() ? getTaskbarScaleOnHome() : 1f);
+ updateWhichTaskbarViewIsVisible();
}
private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
return new TaskbarStateHandlerCallbacks() {
@Override
public AnimatedFloat getAlphaTarget() {
- return mTaskbarVisibilityController.getTaskbarVisibilityForLauncherState();
+ return mTaskbarAnimationController.getTaskbarVisibilityForLauncherState();
+ }
+
+ @Override
+ public AnimatedFloat getScaleTarget() {
+ return mTaskbarAnimationController.getTaskbarScaleForLauncherState();
}
};
}
@@ -273,11 +276,12 @@
mAnimator.end();
}
- mTaskbarView.cleanup();
+ mTaskbarViewInApp.cleanup();
+ mTaskbarViewOnHome.cleanup();
mTaskbarContainerView.cleanup();
removeFromWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(null);
- mTaskbarVisibilityController.cleanup();
+ mTaskbarAnimationController.cleanup();
mHotseatController.cleanup();
mRecentsController.cleanup();
}
@@ -312,7 +316,7 @@
TaskbarContainerView.LayoutParams taskbarLayoutParams =
new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y);
taskbarLayoutParams.gravity = gravity;
- mTaskbarView.setLayoutParams(taskbarLayoutParams);
+ mTaskbarViewInApp.setLayoutParams(taskbarLayoutParams);
mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
}
@@ -329,7 +333,6 @@
mAnimator = createAnimToLauncher(null, duration);
} else {
mAnimator = createAnimToApp(duration);
- replaceTaskbarWithHotseatOrViceVersa();
}
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -346,40 +349,42 @@
*/
public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
PendingAnimation anim = new PendingAnimation(duration);
- anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(0, duration));
+ anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(0, duration));
if (toState != null) {
mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
}
- anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(),
- getTaskbarScaleOnHome(), LINEAR);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mIsAnimatingToLauncher = true;
- mTaskbarView.updateHotseatItemsVisibility();
+ mTaskbarViewInApp.updateHotseatItemsVisibility();
}
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimatingToLauncher = false;
+ updateWhichTaskbarViewIsVisible();
}
});
- anim.addOnFrameCallback(this::alignRealHotseatWithTaskbar);
-
return anim.buildAnim();
}
private Animator createAnimToApp(long duration) {
PendingAnimation anim = new PendingAnimation(duration);
- anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration));
- anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(), 1f, LINEAR);
+ anim.add(mTaskbarAnimationController.createAnimToBackgroundAlpha(1, duration));
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mTaskbarView.updateHotseatItemsVisibility();
- setReplaceTaskbarWithHotseat(false);
+ mIsAnimatingToApp = true;
+ mTaskbarViewInApp.updateHotseatItemsVisibility();
+ updateWhichTaskbarViewIsVisible();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingToApp = false;
}
});
return anim.buildAnim();
@@ -389,7 +394,7 @@
* Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
*/
public void setIsImeVisible(boolean isImeVisible) {
- mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+ mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
}
/**
@@ -404,11 +409,11 @@
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/
public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
- return mTaskbarView.isEventOverAnyItem(ev);
+ return mTaskbarViewInApp.isEventOverAnyItem(ev);
}
public boolean isDraggingItem() {
- return mTaskbarView.isDraggingItem();
+ return mTaskbarViewInApp.isDraggingItem() || mTaskbarViewOnHome.isDraggingItem();
}
private void dedupeAndUpdateRecentItems() {
@@ -453,7 +458,8 @@
tasksArray[tasksArray.length - 1 - i] = task;
}
- mTaskbarView.updateRecentTasks(tasksArray);
+ mTaskbarViewInApp.updateRecentTasks(tasksArray);
+ mTaskbarViewOnHome.updateRecentTasks(tasksArray);
mRecentsController.loadIconsForTasks(tasksArray);
}
@@ -470,43 +476,40 @@
*/
public void alignRealHotseatWithTaskbar() {
Rect hotseatBounds = new Rect();
- mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds);
- mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top,
- mTaskbarView.getWidth() - hotseatBounds.right,
- mTaskbarView.getHeight() - hotseatBounds.bottom);
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
+ int hotseatTopDiff = hotseatHeight - grid.taskbarSize;
+
+ mTaskbarViewOnHome.getHotseatBounds().roundOut(hotseatBounds);
+ mLauncher.getHotseat().setPadding(hotseatBounds.left,
+ hotseatBounds.top + hotseatTopDiff,
+ mTaskbarViewOnHome.getWidth() - hotseatBounds.right,
+ mTaskbarViewOnHome.getHeight() - hotseatBounds.bottom);
+ }
+
+ private void updateWhichTaskbarViewIsVisible() {
+ boolean isInApp = !mLauncher.hasBeenResumed() || mIsAnimatingToLauncher
+ || mIsAnimatingToApp;
+ if (isInApp) {
+ mTaskbarViewInApp.setVisibility(View.VISIBLE);
+ mTaskbarViewOnHome.setVisibility(View.INVISIBLE);
+ mLauncher.getHotseat().setIconsAlpha(0);
+ } else {
+ mTaskbarViewInApp.setVisibility(View.INVISIBLE);
+ mTaskbarViewOnHome.setVisibility(View.VISIBLE);
+ mLauncher.getHotseat().setIconsAlpha(1);
+ }
}
/**
- * A view was added or removed from DragLayer, check if we need to hide our hotseat copy and
- * show the real one instead.
+ * Returns the ratio of the taskbar icon size on home vs in an app.
*/
- public void onLauncherDragLayerHierarchyChanged() {
- replaceTaskbarWithHotseatOrViceVersa();
- }
-
- private void replaceTaskbarWithHotseatOrViceVersa() {
- boolean replaceTaskbarWithHotseat = AbstractFloatingView.getTopOpenViewWithType(mLauncher,
- TYPE_ALL & TYPE_REPLACE_TASKBAR_WITH_HOTSEAT) != null;
- if (!mLauncher.hasBeenResumed()) {
- replaceTaskbarWithHotseat = false;
- }
- setReplaceTaskbarWithHotseat(replaceTaskbarWithHotseat);
- }
-
- private void setReplaceTaskbarWithHotseat(boolean replaceTaskbarWithHotseat) {
- Hotseat hotseat = mLauncher.getHotseat();
- if (replaceTaskbarWithHotseat) {
- alignRealHotseatWithTaskbar();
- hotseat.getReplaceTaskbarAlpha().setValue(1f);
- mTaskbarView.setHotseatViewsHidden(true);
- } else {
- hotseat.getReplaceTaskbarAlpha().setValue(0f);
- mTaskbarView.setHotseatViewsHidden(false);
- }
- }
-
- private float getTaskbarScaleOnHome() {
- return 1f / mTaskbarContainerView.getTaskbarActivityContext().getTaskbarIconScale();
+ public float getTaskbarScaleOnHome() {
+ DeviceProfile inAppDp = mTaskbarContainerView.getTaskbarActivityContext()
+ .getDeviceProfile();
+ DeviceProfile onHomeDp = ActivityContext.lookupContext(mTaskbarViewOnHome.getContext())
+ .getDeviceProfile();
+ return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
}
/**
@@ -528,15 +531,17 @@
*/
protected interface TaskbarStateHandlerCallbacks {
AnimatedFloat getAlphaTarget();
+ AnimatedFloat getScaleTarget();
}
/**
- * Contains methods that TaskbarVisibilityController can call to interface with
+ * Contains methods that TaskbarAnimationController can call to interface with
* TaskbarController.
*/
- protected interface TaskbarVisibilityControllerCallbacks {
+ protected interface TaskbarAnimationControllerCallbacks {
void updateTaskbarBackgroundAlpha(float alpha);
void updateTaskbarVisibilityAlpha(float alpha);
+ void updateTaskbarScale(float scale);
}
/**
@@ -552,7 +557,10 @@
protected interface TaskbarViewCallbacks {
View.OnClickListener getItemOnClickListener();
View.OnLongClickListener getItemOnLongClickListener();
- int getEmptyHotseatViewVisibility();
+ int getEmptyHotseatViewVisibility(TaskbarView taskbarView);
+ /** Returns how much to scale non-icon elements such as spacing and dividers. */
+ float getNonIconScale(TaskbarView taskbarView);
+ void onItemPositionsChanged(TaskbarView taskbarView);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index f51e498..5eb34cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -33,7 +33,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ClipDescriptionCompat;
@@ -126,38 +125,6 @@
}
/**
- * Starts a drag and drop operation that controls a real Workspace (Hotseat) view.
- * @param view The Taskbar item that was long clicked.
- * @return Whether {@link View#startDragAndDrop} started successfully.
- */
- protected boolean startWorkspaceDragOnLongClick(View view) {
- View.DragShadowBuilder transparentShadowBuilder = new View.DragShadowBuilder(view) {
- private static final int ARBITRARY_SHADOW_SIZE = 10;
-
- @Override
- public void onDrawShadow(Canvas canvas) {
- }
-
- @Override
- public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
- outShadowSize.set(ARBITRARY_SHADOW_SIZE, ARBITRARY_SHADOW_SIZE);
- outShadowTouchPoint.set(ARBITRARY_SHADOW_SIZE / 2, ARBITRARY_SHADOW_SIZE / 2);
- }
- };
-
- TaskbarDragListener taskbarDragListener = new TaskbarDragListener(mLauncher,
- (ItemInfo) view.getTag());
- if (view.startDragAndDrop(new ClipData("", new String[] {taskbarDragListener.getMimeType()},
- new ClipData.Item("")),
- transparentShadowBuilder, null /* localState */, View.DRAG_FLAG_GLOBAL)) {
- view.setOnDragListener(getDraggedViewDragListener());
- taskbarDragListener.init(mLauncher.getDragLayer());
- return true;
- }
- return false;
- }
-
- /**
* Hide the original Taskbar item while it is being dragged.
*/
private View.OnDragListener getDraggedViewDragListener() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java
deleted file mode 100644
index dc27df1..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import android.content.ClipDescription;
-import android.graphics.Point;
-import android.view.DragEvent;
-import android.view.View;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.ItemInfo;
-
-import java.util.UUID;
-
-/**
- * Listens to system drag and drop events initated by the Taskbar, and forwards them to Launcher's
- * internal DragController to move Hotseat items.
- */
-public class TaskbarDragListener implements View.OnDragListener {
-
- private static final String MIME_TYPE_PREFIX = "com.android.launcher3.taskbar.drag_and_drop/";
-
- private final BaseQuickstepLauncher mLauncher;
- private final ItemInfo mDraggedItem;
- // Randomly generated id used to verify the drag event.
- private final String mId;
-
- // Initialized in init().
- DragLayer mDragLayer;
-
- /**
- * @param draggedItem The info of the item that was long clicked, which we will use to find
- * the equivalent match on Hotseat to drag internally.
- */
- public TaskbarDragListener(BaseQuickstepLauncher launcher, ItemInfo draggedItem) {
- mLauncher = launcher;
- mDraggedItem = draggedItem;
- mId = UUID.randomUUID().toString();
- }
-
- protected void init(DragLayer dragLayer) {
- mDragLayer = dragLayer;
- mDragLayer.setOnDragListener(this);
- // Temporarily disable haptics, as system will already play one when drag and drop starts.
- mDragLayer.setHapticFeedbackEnabled(false);
- }
-
- private void cleanup() {
- mDragLayer.setOnDragListener(null);
- mLauncher.setNextWorkspaceDragOptions(null);
- mDragLayer.setHapticFeedbackEnabled(true);
- }
-
- /**
- * Returns a randomly generated id used to verify the drag event.
- */
- protected String getMimeType() {
- return MIME_TYPE_PREFIX + mId;
- }
-
- @Override
- public boolean onDrag(View dragLayer, DragEvent dragEvent) {
- ClipDescription clipDescription = dragEvent.getClipDescription();
- if (dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) {
- if (clipDescription == null || !clipDescription.hasMimeType(getMimeType())) {
- // We didn't initiate this drag, ignore.
- cleanup();
- return false;
- }
- View hotseatView = mLauncher.getHotseat().getFirstItemMatch(
- (info, view) -> info == mDraggedItem);
- if (hotseatView == null) {
- cleanup();
- return false;
- }
- DragOptions dragOptions = new DragOptions();
- dragOptions.simulatedDndStartPoint = new Point((int) dragEvent.getX(),
- (int) dragEvent.getY());
- mLauncher.setNextWorkspaceDragOptions(dragOptions);
- hotseatView.performLongClick();
- } else if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENDED) {
- cleanup();
- }
- return mLauncher.getDragController().onDragEvent(dragEvent);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
index b4b5d8b..9fc7d99 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -16,14 +16,12 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherState.TASKBAR;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
@@ -55,8 +53,10 @@
}
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
+ scaleTarget.updateValue(state.getTaskbarScale(mLauncher));
}
@Override
@@ -65,13 +65,12 @@
if (mTaskbarCallbacks == null) {
return;
}
- if (config.hasAnimationFlag(SKIP_TASKBAR)) {
- return;
- }
AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ AnimatedFloat scaleTarget = mTaskbarCallbacks.getScaleTarget();
boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
- animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f,
- config.getInterpolator(ANIM_TASKBAR_FADE, Interpolators.LINEAR));
+ animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f, LINEAR);
+ animation.setFloat(scaleTarget, AnimatedFloat.VALUE, toState.getTaskbarScale(mLauncher),
+ LINEAR);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 1d762e9..21a2d51 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,11 +15,14 @@
*/
package com.android.launcher3.taskbar;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Matrix;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -36,6 +39,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.folder.FolderIcon;
@@ -48,20 +52,23 @@
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
-public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent {
+public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
private final ColorDrawable mBackgroundDrawable;
- private final int mItemMarginLeftRight;
+ private final int mDividerWidth;
+ private final int mDividerHeight;
private final int mIconTouchSize;
private final boolean mIsRtl;
private final int mTouchSlop;
private final RectF mTempDelegateBounds = new RectF();
private final RectF mDelegateSlopBounds = new RectF();
private final int[] mTempOutLocation = new int[2];
- private final Matrix mTempMatrix = new Matrix();
// Initialized in TaskbarController constructor.
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+ // Scale on elements that aren't icons.
+ private float mNonIconScale;
+ private int mItemMarginLeftRight;
// Initialized in init().
private LayoutTransition mLayoutTransition;
@@ -78,7 +85,6 @@
private boolean mIsDraggingItem;
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
- private boolean mIsHotseatHidden;
public TaskbarView(@NonNull Context context) {
this(context, null);
@@ -99,7 +105,8 @@
Resources resources = getResources();
mBackgroundDrawable = (ColorDrawable) getBackground();
- mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ mDividerWidth = resources.getDimensionPixelSize(R.dimen.taskbar_divider_thickness);
+ mDividerHeight = resources.getDimensionPixelSize(R.dimen.taskbar_divider_height);
mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
mIsRtl = Utilities.isRtl(resources);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
@@ -107,12 +114,12 @@
protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
mControllerCallbacks = taskbarViewCallbacks;
+ mNonIconScale = mControllerCallbacks.getNonIconScale(this);
+ mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
}
protected void init(int numHotseatIcons, int numRecentIcons) {
- mLayoutTransition = new LayoutTransition();
- setLayoutTransitionsEnabled(true);
-
mHotseatStartIndex = 0;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
@@ -123,14 +130,50 @@
mRecentsStartIndex = dividerIndex + 1;
mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
updateRecentTasks(new Task[numRecentIcons]);
+
+ mLayoutTransition = new LayoutTransition();
+ addUpdateListenerForAllLayoutTransitions(() -> {
+ if (getLayoutTransition() == mLayoutTransition) {
+ mControllerCallbacks.onItemPositionsChanged(this);
+ }
+ });
+ setLayoutTransition(mLayoutTransition);
}
- private void setLayoutTransitionsEnabled(boolean enabled) {
- setLayoutTransition(enabled ? mLayoutTransition : null);
+ private void addUpdateListenerForAllLayoutTransitions(Runnable onUpdate) {
+ addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_APPEARING, onUpdate);
+ addUpdateListenerForLayoutTransition(LayoutTransition.CHANGE_DISAPPEARING, onUpdate);
+ addUpdateListenerForLayoutTransition(LayoutTransition.CHANGING, onUpdate);
+ addUpdateListenerForLayoutTransition(LayoutTransition.APPEARING, onUpdate);
+ addUpdateListenerForLayoutTransition(LayoutTransition.DISAPPEARING, onUpdate);
+ }
+
+ private void addUpdateListenerForLayoutTransition(int transitionType, Runnable onUpdate) {
+ Animator anim = mLayoutTransition.getAnimator(transitionType);
+ if (anim instanceof ValueAnimator) {
+ ((ValueAnimator) anim).addUpdateListener(valueAnimator -> onUpdate.run());
+ } else {
+ AnimatorSet animSet = new AnimatorSet();
+ ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
+ updateAnim.addUpdateListener(valueAnimator -> onUpdate.run());
+ animSet.playTogether(anim, updateAnim);
+ mLayoutTransition.setAnimator(transitionType, animSet);
+ }
}
protected void cleanup() {
+ endAllLayoutTransitionAnimators();
+ setLayoutTransition(null);
removeAllViews();
+ mHotseatRecentsDivider = null;
+ }
+
+ private void endAllLayoutTransitionAnimators() {
+ mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING).end();
+ mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING).end();
+ mLayoutTransition.getAnimator(LayoutTransition.CHANGING).end();
+ mLayoutTransition.getAnimator(LayoutTransition.APPEARING).end();
+ mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING).end();
}
/**
@@ -170,12 +213,11 @@
if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
|| needsReinflate) {
removeView(hotseatView);
- TaskbarActivityContext activityContext =
- ActivityContext.lookupContext(getContext());
+ ActivityContext activityContext = ActivityContext.lookupContext(getContext());
if (isFolder) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
- activityContext, this, folderInfo);
+ ActivityContext.lookupContext(getContext()), this, folderInfo);
folderIcon.setTextVisible(false);
hotseatView = folderIcon;
} else {
@@ -216,22 +258,12 @@
}
}
- /**
- * Hides or shows the hotseat items immediately (without layout transitions).
- */
- protected void setHotseatViewsHidden(boolean hidden) {
- mIsHotseatHidden = hidden;
- setLayoutTransitionsEnabled(false);
- updateHotseatItemsVisibility();
- setLayoutTransitionsEnabled(true);
- }
-
private void updateHotseatItemVisibility(View hotseatView) {
if (hotseatView.getTag() != null) {
- hotseatView.setVisibility(mIsHotseatHidden ? INVISIBLE : VISIBLE);
+ hotseatView.setVisibility(VISIBLE);
} else {
int oldVisibility = hotseatView.getVisibility();
- int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility();
+ int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility(this);
hotseatView.setVisibility(newVisibility);
if (oldVisibility == GONE && newVisibility != GONE) {
// By default, the layout transition only runs when going to VISIBLE,
@@ -243,7 +275,11 @@
private View addDivider(int dividerIndex) {
View divider = inflate(R.layout.taskbar_divider);
- addView(divider, dividerIndex);
+ LayoutParams lp = new LayoutParams(mDividerWidth, mDividerHeight);
+ lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+ divider.setScaleX(mNonIconScale);
+ divider.setScaleY(mNonIconScale);
+ addView(divider, dividerIndex, lp);
return divider;
}
@@ -437,9 +473,9 @@
}
/**
- * @return The bounding box of where the hotseat elements will be when we reach the given scale.
+ * @return The bounding box of where the hotseat elements are relative to this TaskbarView.
*/
- protected RectF getHotseatBoundsAtScale(float taskbarViewScale) {
+ protected RectF getHotseatBounds() {
View firstHotseatView = null, lastHotseatView = null;
for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
View child = getChildAt(i);
@@ -455,14 +491,11 @@
}
View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView;
View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView;
- RectF hotseatBounds = new RectF(
+ return new RectF(
leftmostHotseatView.getLeft() - mItemMarginLeftRight,
leftmostHotseatView.getTop(),
rightmostHotseatView.getRight() + mItemMarginLeftRight,
rightmostHotseatView.getBottom());
- mTempMatrix.setScale(taskbarViewScale, taskbarViewScale, getPivotX(), getPivotY());
- mTempMatrix.mapRect(hotseatBounds);
- return hotseatBounds;
}
// FolderIconParent implemented methods.
@@ -493,7 +526,12 @@
}
private View inflate(@LayoutRes int layoutResId) {
- TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext());
- return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+ return ActivityContext.lookupContext(getContext()).getLayoutInflater()
+ .inflate(layoutResId, this, false);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ // Ignore, we just implement Insettable to draw behind system insets.
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index bedaefa..d36af09 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -26,8 +26,6 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
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.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
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.RECENTS_GRID_PROGRESS;
@@ -76,17 +74,13 @@
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
SCRIM_MULTIPLIER.set(scrim, 1f);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
- RECENTS_GRID_PROGRESS.set(mRecentsView, state.displayOverviewTasksAsGrid(mLauncher)
- ? 1f : 0f);
+ RECENTS_GRID_PROGRESS.set(mRecentsView,
+ state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
}
@Override
public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
PendingAnimation builder) {
- if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
- // The entire recents animation is played atomically.
- return;
- }
if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return;
}
@@ -128,7 +122,7 @@
toState.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
- toState.displayOverviewTasksAsGrid(mLauncher) ? 1f : 0f, LINEAR);
+ toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f, LINEAR);
}
abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index d330a68..b4aa596 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -34,11 +34,11 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
@@ -85,8 +85,13 @@
public void onDraw(Canvas canvas) {
int count = canvas.save();
if (!mIsPinned) {
- boolean isBadged = getTag() instanceof WorkspaceItemInfo
- && !Process.myUserHandle().equals(((ItemInfo) getTag()).user);
+ boolean isBadged = false;
+ if (getTag() instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
+ isBadged = !Process.myUserHandle().equals(info.user)
+ || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+ || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+ }
drawEffect(canvas, isBadged);
canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index e02f2c2..0ceb8c7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -35,6 +36,7 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.view.HapticFeedbackConstants;
import android.view.View;
import com.android.launcher3.BaseQuickstepLauncher;
@@ -234,7 +236,6 @@
@Override
public void onDestroy() {
super.onDestroy();
- getAppsView().getSearchUiManager().destroy();
mHotseatPredictionController.destroy();
}
@@ -251,6 +252,11 @@
}
break;
}
+ case HINT_STATE_TWO_BUTTON_ORDINAL: {
+ getStateManager().goToState(OVERVIEW);
+ getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ break;
+ }
case OVERVIEW_STATE_ORDINAL: {
RecentsView rv = getOverviewPanel();
sendCustomAccessibilityEvent(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 750f673..e1456b1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -25,6 +25,7 @@
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
+import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import android.annotation.TargetApi;
import android.os.Build;
@@ -73,7 +74,7 @@
if (toState.overviewUi) {
// While animating into recents, update the visible task data as needed
- builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+ builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
} else {
builder.addListener(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
index 36c0e34..1417995 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
@@ -102,9 +102,11 @@
private void notifyChange() {
// Create a new array to avoid concurrent modification when the activity destroys itself.
mTempListeners = mListeners.toArray(mTempListeners);
- for (OnChangeListener listener : mTempListeners) {
+ for (int i = mTempListeners.length - 1; i >= 0; --i) {
+ final OnChangeListener listener = mTempListeners[i];
if (listener != null) {
listener.onExtractedColorsChanged(this);
+ mTempListeners[i] = null;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 37c774b..b2f8a40 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -78,7 +78,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+ return ALL_APPS_CONTENT;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 2ad718b..aa770d2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -20,6 +20,7 @@
import android.content.Context;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.quickstep.util.LayoutUtils;
@@ -76,25 +77,11 @@
}
@Override
- public boolean displayOverviewTasksAsGrid(Launcher launcher) {
+ public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
return false;
}
@Override
- public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
- if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
- // Translate hotseat offscreen if we show it in overview.
- RecentsView recentsView = launcher.getOverviewPanel();
- ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
- scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher,
- launcher.getDeviceProfile(),
- recentsView.getPagedOrientationHandler());
- return scaleAndTranslation;
- }
- return super.getHotseatScaleAndTranslation(launcher);
- }
-
- @Override
protected float getDepthUnchecked(Context context) {
return 1f;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 372784a..43e70a3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -78,37 +78,13 @@
}
@Override
- public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
- if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
- DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.allAppsIconSizePx >= dp.iconSizePx) {
- return new ScaleAndTranslation(1, 0, 0);
- } else {
- float scale = ((float) dp.allAppsIconSizePx) / dp.iconSizePx;
- // Distance between the screen center (which is the pivotY for hotseat) and the
- // bottom of the hotseat (which we want to preserve)
- float distanceFromBottom = dp.heightPx / 2 - dp.hotseatBarBottomPaddingPx;
- // On scaling, the bottom edge is moved closer to the pivotY. We move the
- // hotseat back down so that the bottom edge's position is preserved.
- float translationY = distanceFromBottom * (1 - scale);
- return new ScaleAndTranslation(scale, 0, translationY);
- }
- }
- return getWorkspaceScaleAndTranslation(launcher);
- }
-
- @Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
return new float[] {NO_SCALE, NO_OFFSET};
}
@Override
- public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
- if (this == OVERVIEW) {
- // Treat the QSB as part of the hotseat so they move together.
- return getHotseatScaleAndTranslation(launcher);
- }
- return super.getQsbScaleAndTranslation(launcher);
+ public float getTaskbarScale(Launcher launcher) {
+ return 1f;
}
@Override
@@ -123,7 +99,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return displayOverviewTasksAsGrid(launcher) ? CLEAR_ALL_BUTTON
+ return displayOverviewTasksAsGrid(launcher.getDeviceProfile()) ? CLEAR_ALL_BUTTON
: CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
}
@@ -133,8 +109,8 @@
}
@Override
- public boolean displayOverviewTasksAsGrid(Launcher launcher) {
- return launcher.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+ public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
+ return deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 473fe2d..ba61923 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -18,7 +18,7 @@
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherState.HINT_STATE;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
@@ -33,12 +33,10 @@
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_HOTSEAT_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_TASKBAR_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;
@@ -82,7 +80,6 @@
if (toState == NORMAL && fromState == OVERVIEW) {
config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
- config.setInterpolator(ANIM_TASKBAR_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);
@@ -119,7 +116,8 @@
qsbContainer.setScaleY(0.92f);
}
}
- } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
+ } else if ((fromState == NORMAL || fromState == HINT_STATE
+ || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
config.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
@@ -141,7 +139,6 @@
config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_TASKBAR_FADE, OVERSHOOT_1_2);
} else if (fromState == HINT_STATE && toState == NORMAL) {
config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
if (mHintToNormalDuration == -1) {
@@ -150,17 +147,6 @@
mHintToNormalDuration = (int) va.getDuration();
}
config.duration = Math.max(config.duration, mHintToNormalDuration);
- } else if (mActivity.getTaskbarController() != null) {
- boolean wasHotseatVisible = fromState.areElementsVisible(mActivity, HOTSEAT_ICONS);
- boolean isHotseatVisible = toState.areElementsVisible(mActivity, HOTSEAT_ICONS);
- if (wasHotseatVisible || isHotseatVisible) {
- config.setInterpolator(ANIM_TASKBAR_FADE, INSTANT);
- config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT);
-
- if (isHotseatVisible) {
- mActivity.getTaskbarController().alignRealHotseatWithTaskbar();
- }
- }
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 45cb46f..e70450d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -108,8 +108,8 @@
}
@Override
- protected float initCurrentAnimation(int animComponents) {
- float progressMultiplier = super.initCurrentAnimation(animComponents);
+ protected float initCurrentAnimation() {
+ float progressMultiplier = super.initCurrentAnimation();
if (mToState == HINT_STATE) {
// Track the drag across the entire height of the screen.
progressMultiplier = -1 / getShiftRange();
@@ -155,7 +155,7 @@
super.onDragEnd(velocity);
}
- View searchView = mLauncher.getAppsView().getSearchView();
+ View searchView = mLauncher.getHotseat().getQsb();
if (searchView instanceof FeedbackHandler) {
((FeedbackHandler) searchView).resetFeedback();
}
@@ -203,7 +203,7 @@
}
private void maybeSwipeInteractionToOverviewComplete() {
- if (mReachedOverview && mDetector.isSettlingState()) {
+ if (mReachedOverview && !mDetector.isDraggingState()) {
onSwipeInteractionCompleted(OVERVIEW);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 4766870..61cd13b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -33,6 +33,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
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.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
@@ -372,7 +373,7 @@
// animation as from an app.
StateAnimationConfig config = new StateAnimationConfig();
// Update mNonOverviewAnim to do nothing so it doesn't interfere.
- config.animFlags = 0;
+ config.animFlags = SKIP_ALL_ANIMATIONS;
updateNonOverviewAnim(targetState, config);
nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index facfb9d..1af9685 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -23,17 +23,9 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.util.Log;
import android.view.MotionEvent;
-import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -41,8 +33,6 @@
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
@@ -70,11 +60,6 @@
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
- private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
-
- // If true, we will finish the current animation instantly on second touch.
- private boolean mFinishFastOnSecondTouch;
-
public PortraitStatesTouchController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
@@ -85,10 +70,6 @@
// If we are swiping to all apps instead of overview, allow it from anywhere.
boolean interceptAnywhere = mLauncher.isInState(NORMAL);
if (mCurrentAnimation != null) {
- if (mFinishFastOnSecondTouch) {
- mCurrentAnimation.getAnimationPlayer().end();
- }
-
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()
|| interceptAnywhere) {
@@ -96,11 +77,7 @@
// the touch is below the current all apps progress (to allow for double swipe).
return true;
}
- // Otherwise, make sure everything is settled and don't intercept so they can scroll
- // recents, dismiss a task, etc.
- if (mAtomicAnim != null) {
- mAtomicAnim.end();
- }
+ // Otherwise, don't intercept so they can scroll recents, dismiss a task, etc.
return false;
}
if (mLauncher.isInState(ALL_APPS)) {
@@ -136,32 +113,6 @@
return fromState;
}
- private StateAnimationConfig getNormalToOverviewAnimation() {
- mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
-
- StateAnimationConfig builder = new StateAnimationConfig();
- builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
- return builder;
- }
-
- private static StateAnimationConfig getOverviewToAllAppsAnimation() {
- StateAnimationConfig builder = new StateAnimationConfig();
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
- 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
- builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
- RECENTS_FADE_THRESHOLD, 1));
- return builder;
- }
-
- private StateAnimationConfig getAllAppsToOverviewAnimation() {
- StateAnimationConfig builder = new StateAnimationConfig();
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
- 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
- builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
- 0f, 1 - RECENTS_FADE_THRESHOLD));
- return builder;
- }
-
private StateAnimationConfig getNormalToAllAppsAnimation() {
StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
@@ -180,13 +131,7 @@
protected StateAnimationConfig getConfigForStates(
LauncherState fromState, LauncherState toState) {
final StateAnimationConfig config;
- if (fromState == NORMAL && toState == OVERVIEW) {
- config = getNormalToOverviewAnimation();
- } else if (fromState == OVERVIEW && toState == ALL_APPS) {
- config = getOverviewToAllAppsAnimation();
- } else if (fromState == ALL_APPS && toState == OVERVIEW) {
- config = getAllAppsToOverviewAnimation();
- } else if (fromState == NORMAL && toState == ALL_APPS) {
+ if (fromState == NORMAL && toState == ALL_APPS) {
config = getNormalToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == NORMAL) {
config = getAllAppsToNormalAnimation();
@@ -197,7 +142,7 @@
}
@Override
- protected float initCurrentAnimation(@AnimationFlags int animFlags) {
+ protected float initCurrentAnimation() {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
@@ -208,7 +153,6 @@
final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
: getConfigForStates(mFromState, mToState);
- config.animFlags = animFlags;
config.duration = maxAccuracy;
if (mCurrentAnimation != null) {
@@ -243,35 +187,6 @@
}
@Override
- protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
- LauncherState targetState, float velocity, boolean isFling) {
- super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
- velocity, isFling);
- handleFirstSwipeToOverview(animator, expectedDuration, targetState, velocity, isFling);
- }
-
- private void handleFirstSwipeToOverview(final ValueAnimator animator,
- final long expectedDuration, final LauncherState targetState, final float velocity,
- final boolean isFling) {
- if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
- && targetState == OVERVIEW) {
- mFinishFastOnSecondTouch = true;
- } else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
- mFinishFastOnSecondTouch = true;
- if (isFling && expectedDuration != 0) {
- // Update all apps interpolator to add a bit of overshoot starting from currFraction
- final float currFraction = mCurrentAnimation.getProgressFraction();
- mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
- Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
- animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
- .setInterpolator(LINEAR);
- }
- } else {
- mFinishFastOnSecondTouch = false;
- }
- }
-
- @Override
protected void onSwipeInteractionCompleted(LauncherState targetState) {
super.onSwipeInteractionCompleted(targetState);
if (mStartState == NORMAL && targetState == OVERVIEW) {
@@ -296,16 +211,6 @@
return launcher.getDragLayer().getHeight() - hotseatHeight;
}
- private static class InterpolatorWrapper implements Interpolator {
-
- public TimeInterpolator baseInterpolator = LINEAR;
-
- @Override
- public float getInterpolation(float v) {
- return baseInterpolator.getInterpolation(v);
- }
- }
-
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index fc9e1bb..8542209 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -101,7 +101,7 @@
}
@Override
- protected float initCurrentAnimation(int animComponents) {
+ protected float initCurrentAnimation() {
StateAnimationConfig config = new StateAnimationConfig();
setupInterpolators(config);
config.duration = (long) (getShiftRange() * 2);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index 0ed5291..8f9c014 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -32,8 +32,8 @@
}
@Override
- protected float initCurrentAnimation(int animComponents) {
- float multiplier = super.initCurrentAnimation(animComponents);
+ protected float initCurrentAnimation() {
+ float multiplier = super.initCurrentAnimation();
return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
index faf5054..3f7190f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -17,17 +17,18 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.AbstractFloatingView.getOpenView;
+import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import android.animation.ValueAnimator;
+import android.os.SystemClock;
import android.view.MotionEvent;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.quickstep.SystemUiProxy;
@@ -91,10 +92,10 @@
if (mIsTransposed) {
boolean draggingFromNav =
mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
- return draggingFromNav ? OVERVIEW : NORMAL;
+ return draggingFromNav ? HINT_STATE_TWO_BUTTON : NORMAL;
} else {
LauncherState startState = mStartState != null ? mStartState : fromState;
- return isDragTowardPositive ^ (startState == OVERVIEW) ? OVERVIEW : NORMAL;
+ return isDragTowardPositive ^ (startState == OVERVIEW) ? HINT_STATE_TWO_BUTTON : NORMAL;
}
}
@@ -104,6 +105,12 @@
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
velocity, isFling);
mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
+
+ if (targetState == HINT_STATE_TWO_BUTTON) {
+ // We were going to HINT_STATE_TWO_BUTTON, but end that animation immediately so we go
+ // to OVERVIEW instead.
+ animator.setDuration(0);
+ }
}
@Override
@@ -113,21 +120,35 @@
}
@Override
- protected float initCurrentAnimation(@AnimationFlags int animComponent) {
+ protected float initCurrentAnimation() {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
- maxAccuracy, animComponent);
+ maxAccuracy);
return (mLauncher.getDeviceProfile().isSeascape() ? 1 : -1) / range;
}
@Override
+ protected void updateProgress(float fraction) {
+ super.updateProgress(fraction);
+
+ // We have reached HINT_STATE, end the gesture now to go to OVERVIEW.
+ if (fraction >= 1 && mToState == HINT_STATE_TWO_BUTTON) {
+ final long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
+ mDetector.onTouchEvent(event);
+ event.recycle();
+ }
+ }
+
+ @Override
protected void onSwipeInteractionCompleted(LauncherState targetState) {
super.onSwipeInteractionCompleted(targetState);
if (!mIsTransposed) {
mContinuousTouchCount++;
}
- if (mStartState == NORMAL && targetState == OVERVIEW) {
+ if (mStartState == NORMAL && targetState == HINT_STATE_TWO_BUTTON) {
SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
} else if (targetState == NORMAL
&& mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 7df86b9..6759936 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -46,8 +45,11 @@
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
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;
import android.animation.Animator;
@@ -80,9 +82,9 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
@@ -96,11 +98,12 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
+import com.android.quickstep.util.InputProxyHandlerFactory;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TransformParams;
@@ -112,26 +115,25 @@
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.function.Consumer;
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
@TargetApi(Build.VERSION_CODES.R)
-public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
+public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
+ Q extends RecentsView, S extends BaseState<S>>
extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
RecentsAnimationCallbacks.RecentsAnimationListener {
private static final String TAG = "AbsSwipeUpHandler";
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[17] : null;
- protected final BaseActivityInterface<?, T> mActivityInterface;
+ protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
// Callbacks to be made once the recents animation starts
@@ -198,7 +200,7 @@
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
public static final long MAX_SWIPE_DURATION = 350;
- public static final long MIN_OVERSHOOT_DURATION = 120;
+ public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
private static final float SWIPE_DURATION_MULTIPLIER =
@@ -253,7 +255,10 @@
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumerProxy =
- new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
+ new InputConsumerProxy(inputConsumer, () -> {
+ endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
+ endLauncherTransitionController();
+ }, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -615,7 +620,7 @@
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (!mDeviceState.isFullyGesturalNavMode()) {
+ if (mDeviceState.isTwoButtonNavMode()) {
performHapticFeedback();
}
}
@@ -721,6 +726,9 @@
@UiThread
public void onGestureStarted(boolean isLikelyToStartNewTask) {
+ mActivityInterface.closeOverlay();
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+
if (mRecentsView != null) {
InteractionJankMonitorWrapper.begin(mRecentsView,
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
@@ -778,19 +786,6 @@
handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
- /**
- * Called to create a input proxy for the running task
- */
- @UiThread
- protected InputConsumer createNewInputProxyHandler() {
- endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
- endLauncherTransitionController();
-
- StatefulActivity activity = mActivityInterface.getCreatedActivity();
- return activity == null ? InputConsumer.NO_OP
- : new OverviewInputConsumer(mGestureState, activity, null, true);
- }
-
private void endRunningWindowAnim(boolean cancel) {
if (mRunningWindowAnim != null) {
if (cancel) {
@@ -848,6 +843,10 @@
private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
boolean isCancel) {
+ if (mDeviceState.isButtonNavMode()) {
+ // Button mode, this is only used to go to recents
+ return RECENTS;
+ }
final GestureEndTarget endTarget;
final boolean goingToNewTask;
if (mRecentsView != null) {
@@ -914,6 +913,9 @@
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
isFling, isCancel);
+ // Set the state, but don't notify until the animation completes
+ mGestureState.setEndTarget(endTarget, false /* isAtomic */);
+
float endShift = endTarget.isLauncher ? 1 : 0;
final float startShift;
if (!isFling) {
@@ -934,27 +936,38 @@
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
}
}
- Interpolator interpolator =
- endTarget == RECENTS ? (mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
- ? ACCEL_DEACCEL : OVERSHOOT_1_2) : DEACCEL;
+ Interpolator interpolator;
+ S state = mActivityInterface.stateFromGestureEndTarget(endTarget);
+ if (state.displayOverviewTasksAsGrid(mDp)) {
+ interpolator = ACCEL_DEACCEL;
+ } else if (endTarget == RECENTS) {
+ interpolator = OVERSHOOT_1_2;
+ } else {
+ interpolator = DEACCEL;
+ }
if (endTarget.isLauncher) {
mInputConsumerProxy.enable();
}
if (endTarget == HOME) {
- duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
+ duration = HOME_DURATION;
} else if (endTarget == RECENTS) {
if (mRecentsView != null) {
int nearestPage = mRecentsView.getDestinationPage();
+ boolean isScrolling = false;
if (mRecentsView.getNextPage() != nearestPage) {
// We shouldn't really scroll to the next page when swiping up to recents.
// Only allow settling on the next page if it's nearest to the center.
mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
+ isScrolling = true;
}
if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
+ isScrolling = true;
}
- duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+ if (!mDeviceState.isButtonNavMode() || isScrolling) {
+ duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+ }
}
}
@@ -1035,8 +1048,6 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- // Set the state, but don't notify until the animation completes
- mGestureState.setEndTarget(target, false /* isAtomic */);
maybeUpdateRecentsAttachedState();
// If we are transitioning to launcher, then listen for the activity to be restarted while
@@ -1064,7 +1075,7 @@
homeAnimFactory, runningTaskTarget, start);
mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
mSwipePipToHomeAnimator.setInterpolator(interpolator);
- mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
+ mSwipePipToHomeAnimator.setFloatValues(FRACTION_START, FRACTION_END);
mSwipePipToHomeAnimator.start();
mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
} else {
@@ -1126,10 +1137,9 @@
}
});
animatorSet.play(windowAnim);
- if (mRecentsView != null && mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
- && mGestureState.getEndTarget() == RECENTS) {
+ S state = mActivityInterface.stateFromGestureEndTarget(mGestureState.getEndTarget());
+ if (mRecentsView != null && state.displayOverviewTasksAsGrid(mDp)) {
animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1));
- animatorSet.play(mTaskViewSimulator.gridProgress.animateToValue(0, 1));
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
@@ -1313,7 +1323,12 @@
}
private void invalidateHandler() {
- mInputConsumerProxy.destroy();
+ if (!LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
+ || mGestureState.getEndTarget() != RECENTS) {
+ mInputConsumerProxy.destroy();
+ mTaskAnimationManager.setLiveTileCleanUpHandler(null);
+ }
+ mInputConsumerProxy.unregisterCallback();
endRunningWindowAnim(false /* cancel */);
if (mGestureEndCallback != null) {
@@ -1470,43 +1485,20 @@
mRecentsAnimationController.setFinishTaskBounds(
mSwipePipToHomeAnimator.getTaskId(),
mSwipePipToHomeAnimator.getDestinationBounds(),
- mSwipePipToHomeAnimator.getFinishWindowCrop(),
- mSwipePipToHomeAnimator.getFinishTransform());
+ mSwipePipToHomeAnimator.getFinishTransaction());
mIsSwipingPipToHome = false;
}
}
protected abstract void finishRecentsControllerToHome(Runnable callback);
- private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (mRecentsView.getRunningTaskIndex() != -1
- && mRecentsView.getRunningTaskId() == task.taskId
- && mRecentsAnimationTargets.hasTask(task.taskId)) {
- launchOtherTaskInLiveTileMode(task.taskId, mRecentsAnimationTargets.apps);
- }
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
- mLiveTileRestartListener);
- }
- };
-
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController();
mActivityInterface.onSwipeUpToRecentsComplete();
mRecentsView.onSwipeUpAnimationSuccess();
if (LIVE_TILE.get()) {
- mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
- appearedTaskTarget -> {
- RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
- mRecentsAnimationTargets.apps,
- mRecentsAnimationTargets.apps.length + 1);
- apps[apps.length - 1] = appearedTaskTarget;
- launchOtherTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
- });
- ActivityManagerWrapper.getInstance().registerTaskStackListener(
- mLiveTileRestartListener);
+ mTaskAnimationManager.setLiveTileCleanUpHandler(mInputConsumerProxy::destroy);
+ mTaskAnimationManager.enableLiveTileRestartListener();
}
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
@@ -1514,65 +1506,6 @@
reset();
}
- private void launchOtherTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps) {
- AnimatorSet anim = new AnimatorSet();
- TaskView taskView = mRecentsView.getTaskView(taskId);
- if (taskView == null || !mRecentsView.isTaskViewVisible(taskView)) {
- // TODO: Refine this animation.
- SurfaceTransactionApplier surfaceApplier =
- new SurfaceTransactionApplier(mActivity.getDragLayer());
- ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
- appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
- appAnimator.setInterpolator(ACCEL_DEACCEL);
- appAnimator.addUpdateListener(new MultiValueUpdateListener() {
- @Override
- public void onUpdate(float percent) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder(
- apps[apps.length - 1].leash);
- Matrix matrix = new Matrix();
- matrix.postScale(percent, percent);
- matrix.postTranslate(mDp.widthPx * (1 - percent) / 2,
- mDp.heightPx * (1 - percent) / 2);
- builder.withAlpha(percent).withMatrix(matrix);
- surfaceApplier.scheduleApply(builder.build());
- }
- });
- anim.play(appAnimator);
- } else {
- TaskViewUtils.composeRecentsLaunchAnimator(
- anim, taskView, apps,
- mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
- mActivity.getStateManager(), mRecentsView,
- mActivityInterface.getDepthController());
- }
- anim.addListener(new AnimatorListenerAdapter(){
-
- @Override
- public void onAnimationEnd(Animator animator) {
- cleanUp(false);
- }
-
- @Override
- public void onAnimationCancel(Animator animator) {
- cleanUp(true);
- }
-
- private void cleanUp(boolean canceled) {
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- if (canceled) {
- mRecentsAnimationController = null;
- } else {
- mActivityInterface.onLaunchTaskSuccess();
- }
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
- }
- }
- });
- anim.start();
- }
-
private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
return app.isNotInRecents
|| app.activityType == ACTIVITY_TYPE_HOME;
@@ -1756,7 +1689,6 @@
public interface Factory {
- AbsSwipeUpHandler<StatefulActivity<?>, RecentsView> newHandler(
- GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
+ AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
}
}
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
deleted file mode 100644
index d159fa0..0000000
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.util.Log;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.taskbar.TaskbarController;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TaskViewSimulator;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
- *
- * @param <T> activity that contains the overview
- */
-final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
- RemoteAnimationProvider {
-
- private static final long RECENTS_LAUNCH_DURATION = 250;
- private static final String TAG = "AppToOverviewAnimationProvider";
-
- private final BaseActivityInterface<?, T> mActivityInterface;
- // The id of the currently running task that is transitioning to overview.
- private final RunningTaskInfo mTargetTask;
- private final RecentsAnimationDeviceState mDeviceState;
-
- private T mActivity;
- private RecentsView mRecentsView;
-
- AppToOverviewAnimationProvider(
- BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
- RecentsAnimationDeviceState deviceState) {
- mActivityInterface = activityInterface;
- mTargetTask = targetTask;
- mDeviceState = deviceState;
- }
-
- /**
- * Callback for when the activity is ready/initialized.
- *
- * @param activity the activity that is ready
- * @param wasVisible true if it was visible before
- */
- boolean onActivityReady(T activity, Boolean wasVisible) {
- activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
- AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
- BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
- mDeviceState,
- wasVisible, (controller) -> {
- controller.getNormalController().dispatchOnStart();
- controller.getNormalController().getAnimationPlayer().end();
- });
- factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
- factory.setRecentsAttachedToAppWindow(true, false);
- mActivity = activity;
- mRecentsView = mActivity.getOverviewPanel();
- return false;
- }
-
- /**
- * Create remote window animation from the currently running app to the overview panel.
- *
- * @param appTargets the target apps
- * @return animation from app to overview
- */
- @Override
- public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
- PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
- if (mActivity == null) {
- Log.e(TAG, "Animation created, before activity");
- return pa.buildAnim();
- }
-
- mRecentsView.setRunningTaskIconScaledDown(true);
- pa.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mActivityInterface.onSwipeUpToRecentsComplete();
- mRecentsView.animateUpRunningTaskIconScale();
- }
- });
-
- DepthController depthController = mActivityInterface.getDepthController();
- if (depthController != null) {
- pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
- OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
- }
-
- TaskbarController taskbarController = mActivityInterface.getTaskbarController();
- if (taskbarController != null) {
- pa.add(taskbarController.createAnimToLauncher(OVERVIEW, getRecentsLaunchDuration()));
- }
-
- RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
- wallpaperTargets, MODE_CLOSING);
-
- // Use the top closing app to determine the insets for the animation
- RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
- : targets.findTask(mTargetTask.taskId);
- if (runningTaskTarget == null) {
- Log.e(TAG, "No closing app");
- return pa.buildAnim();
- }
-
- TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
- tsv.setDp(mActivity.getDeviceProfile());
- tsv.setOrientationState(mRecentsView.getPagedViewOrientedState());
- tsv.setPreview(runningTaskTarget);
-
- TransformParams params = new TransformParams()
- .setTargetSet(targets)
- .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView()));
-
- AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
- params.setBaseBuilderProxy((builder, app, p)
- -> builder.withAlpha(recentsAlpha.value));
-
- Interpolator taskInterpolator;
- if (targets.isAnimatingHome()) {
- params.setHomeBuilderProxy((builder, app, p) -> builder.withAlpha(1 - p.getProgress()));
-
- taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
- pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
- } else {
- // When animation from app to recents, the recents layer is drawn on top of the app. To
- // prevent the overlap, we animate the task first and then quickly fade in the recents.
- taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
- pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
- clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
- }
-
- pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
- tsv.addAppToOverviewAnim(pa, taskInterpolator);
- pa.addOnFrameCallback(() -> tsv.apply(params));
- return pa.buildAnim();
- }
-
- /**
- * Get duration of animation from app to overview.
- *
- * @return duration of animation
- */
- long getRecentsLaunchDuration() {
- return RECENTS_LAUNCH_DURATION;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 7c1d9fa..0415009 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -31,6 +31,7 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.view.Gravity;
@@ -43,6 +44,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -195,41 +197,53 @@
}
/**
- * Calculates the taskView size for the provided device configuration
+ * Calculates the taskView size for the provided device configuration.
*/
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
PagedOrientationHandler orientedState) {
Resources res = context.getResources();
+ if (dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ Rect gridRect = new Rect();
+ calculateGridSize(context, dp, gridRect);
- int taskMargin = res.getDimensionPixelSize(R.dimen.overview_task_margin);
- int taskIconAndMargin = res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_size)
- + res.getDimensionPixelSize(R.dimen.task_icon_top_margin);
- int proactiveRowAndMargin = res.getDimensionPixelSize(R.dimen.overview_proactive_row_height)
- + res.getDimensionPixelSize(R.dimen.overview_proactive_row_bottom_margin);
+ int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
+ float rowHeight = (gridRect.height() - rowSpacing) / 2f;
- calculateTaskSizeInternal(context, dp,
- taskIconAndMargin + taskMargin,
- proactiveRowAndMargin + getOverviewActionsHeight(context) + taskMargin,
- res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
- outRect);
+ PointF taskDimension = getTaskDimension(context, dp);
+ float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / Math.max(
+ taskDimension.x, taskDimension.y);
+ int outWidth = Math.round(scale * taskDimension.x);
+ int outHeight = Math.round(scale * taskDimension.y);
+
+ int gravity = Gravity.TOP;
+ gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
+ gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);
+ Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
+ } else {
+ int taskMargin = dp.overviewTaskMarginPx;
+ int proactiveRowAndMargin;
+ if (dp.isVerticalBarLayout()) {
+ // In Vertical Bar Layout the proactive row doesn't have its own space, it's inside
+ // the actions row.
+ proactiveRowAndMargin = 0;
+ } else {
+ proactiveRowAndMargin = res.getDimensionPixelSize(
+ R.dimen.overview_proactive_row_height)
+ + res.getDimensionPixelSize(R.dimen.overview_proactive_row_bottom_margin);
+ }
+ calculateTaskSizeInternal(context, dp,
+ dp.overviewTaskThumbnailTopMarginPx,
+ proactiveRowAndMargin + getOverviewActionsHeight(context) + taskMargin,
+ res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
+ outRect);
+ }
}
private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
int claimedSpaceAbove, int claimedSpaceBelow, int minimumHorizontalPadding,
Rect outRect) {
- float taskWidth, taskHeight;
+ PointF taskDimension = getTaskDimension(context, dp);
Rect insets = dp.getInsets();
- if (dp.isMultiWindowMode) {
- WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
- taskWidth = bounds.availableSize.x;
- taskHeight = bounds.availableSize.y;
- } else if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
- taskWidth = dp.availableWidthPx;
- taskHeight = dp.availableHeightPx;
- } else {
- taskWidth = dp.widthPx;
- taskHeight = dp.heightPx;
- }
Rect potentialTaskRect = new Rect(0, 0, dp.widthPx, dp.heightPx);
potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom);
@@ -240,14 +254,30 @@
claimedSpaceBelow);
float scale = Math.min(
- potentialTaskRect.width() / taskWidth,
- potentialTaskRect.height() / taskHeight);
- int outWidth = Math.round(scale * taskWidth);
- int outHeight = Math.round(scale * taskHeight);
+ potentialTaskRect.width() / taskDimension.x,
+ potentialTaskRect.height() / taskDimension.y);
+ int outWidth = Math.round(scale * taskDimension.x);
+ int outHeight = Math.round(scale * taskDimension.y);
Gravity.apply(Gravity.CENTER, outWidth, outHeight, potentialTaskRect, outRect);
}
+ private PointF getTaskDimension(Context context, DeviceProfile dp) {
+ PointF dimension = new PointF();
+ if (dp.isMultiWindowMode) {
+ WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
+ dimension.x = bounds.availableSize.x;
+ dimension.y = bounds.availableSize.y;
+ } else if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
+ dimension.x = dp.availableWidthPx;
+ dimension.y = dp.availableHeightPx;
+ } else {
+ dimension.x = dp.widthPx;
+ dimension.y = dp.heightPx;
+ }
+ return dimension;
+ }
+
/**
* Calculates the overview grid size for the provided device configuration.
*/
@@ -267,13 +297,11 @@
* Calculates the modal taskView size for the provided device configuration
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
- Resources res = context.getResources();
calculateTaskSizeInternal(
context, dp,
- res.getDimensionPixelSize(R.dimen.overview_task_margin),
- getOverviewActionsHeight(context)
- + res.getDimensionPixelSize(R.dimen.overview_task_margin),
- res.getDimensionPixelSize(R.dimen.overview_task_margin),
+ dp.overviewTaskMarginPx,
+ getOverviewActionsHeight(context) + dp.overviewTaskMarginPx,
+ dp.overviewTaskMarginPx,
outRect);
}
@@ -306,6 +334,11 @@
public void onSystemUiFlagsChanged(int systemUiStateFlags) {
}
+ /**
+ * Returns the expected STATE_TYPE from the provided GestureEndTarget.
+ */
+ public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget);
+
public interface AnimationFactory {
void createActivityInterface(long transitionLength);
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index db290d6..e13d1a4 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -18,6 +18,7 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
+import static com.android.quickstep.fallback.RecentsState.HOME;
import android.content.Context;
import android.graphics.Rect;
@@ -81,6 +82,7 @@
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
+ notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
factory.initUI();
return factory;
@@ -103,7 +105,7 @@
@Override
public RecentsView getVisibleRecentsView() {
RecentsActivity activity = getCreatedActivity();
- if (activity != null && activity.hasWindowFocus()) {
+ if (activity != null && activity.hasBeenResumed()) {
return activity.getOverviewPanel();
}
return null;
@@ -154,4 +156,25 @@
}
activity.<RecentsView>getOverviewPanel().startHome();
}
+
+ @Override
+ public RecentsState stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget) {
+ switch (endTarget) {
+ case RECENTS:
+ return DEFAULT;
+ case NEW_TASK:
+ case LAST_TASK:
+ return BACKGROUND_APP;
+ case HOME:
+ default:
+ return HOME;
+ }
+ }
+
+ private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+ // reset layout on swipe to home
+ RecentsView recentsView = getCreatedActivity().getOverviewPanel();
+ recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+ rotationTouchHelper.getDisplayRotation());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index a80c111..7e4a352 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -55,6 +55,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
@@ -73,7 +74,7 @@
*/
@TargetApi(Build.VERSION_CODES.R)
public class FallbackSwipeHandler extends
- AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
+ AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> {
/**
* Message used for receiving gesture nav contract information. We use a static messenger to
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 8d67ee6..ebdc1e6 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.os.Build;
+import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.tracing.GestureStateProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
@@ -213,7 +214,8 @@
/**
* @return the interface to the activity handing the UI updates for this gesture.
*/
- public <T extends StatefulActivity<?>> BaseActivityInterface<?, T> getActivityInterface() {
+ public <S extends BaseState<S>,
+ T extends StatefulActivity<S>> BaseActivityInterface<S, T> getActivityInterface() {
return mActivityInterface;
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7efbfb8..98b96b2 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -176,7 +177,7 @@
@UiThread
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
- return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus()
+ return (launcher != null) && launcher.isStarted() && launcher.hasBeenResumed()
? launcher : null;
}
@@ -187,6 +188,7 @@
return false;
}
+ closeOverlay();
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
return true;
@@ -270,7 +272,7 @@
if (taskbarController == null) {
return;
}
- LauncherState toState = endTarget == GestureEndTarget.RECENTS ? OVERVIEW : NORMAL;
+ LauncherState toState = stateFromGestureEndTarget(endTarget);
taskbarController.createAnimToLauncher(toState, duration).start();
}
@@ -301,4 +303,18 @@
}
return taskbarController.isDraggingItem();
}
+
+ @Override
+ public LauncherState stateFromGestureEndTarget(GestureEndTarget endTarget) {
+ switch (endTarget) {
+ case RECENTS:
+ return OVERVIEW;
+ case NEW_TASK:
+ case LAST_TASK:
+ return QUICK_SWITCH;
+ case HOME:
+ default:
+ return NORMAL;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 842fb84..311ac83 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -27,7 +27,9 @@
import androidx.annotation.NonNull;
import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -39,7 +41,7 @@
* Temporary class to allow easier refactoring
*/
public class LauncherSwipeHandlerV2 extends
- AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView> {
+ AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView, LauncherState> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
@@ -133,7 +135,7 @@
// to home.
long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
return mActivity.getStateManager().createAnimationToNewWorkspace(
- NORMAL, accuracy, 0 /* animComponents */);
+ NORMAL, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 63fdd0b..923d4f1 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -16,29 +16,28 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.annotation.TargetApi;
-import android.content.Context;
+import android.content.Intent;
+import android.graphics.PointF;
import android.os.Build;
import android.os.SystemClock;
import android.os.Trace;
-import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.ArrayList;
/**
* Helper class to handle various atomic commands for switching between Overview.
@@ -46,66 +45,191 @@
@TargetApi(Build.VERSION_CODES.P)
public class OverviewCommandHelper {
- private final Context mContext;
- private final RecentsAnimationDeviceState mDeviceState;
+ public static final int TYPE_SHOW = 1;
+ public static final int TYPE_SHOW_NEXT_FOCUS = 2;
+ public static final int TYPE_HIDE = 3;
+ public static final int TYPE_TOGGLE = 4;
+
+ private static final String TRANSITION_NAME = "Transition:toOverview";
+
+ private final TouchInteractionService mService;
private final OverviewComponentObserver mOverviewComponentObserver;
+ private final TaskAnimationManager mTaskAnimationManager;
+ private final ArrayList<CommandInfo> mPendingCommands = new ArrayList<>();
- private long mLastToggleTime;
-
- public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
- OverviewComponentObserver observer) {
- mContext = context;
- mDeviceState = deviceState;
+ public OverviewCommandHelper(TouchInteractionService service,
+ OverviewComponentObserver observer,
+ TaskAnimationManager taskAnimationManager) {
+ mService = service;
mOverviewComponentObserver = observer;
+ mTaskAnimationManager = taskAnimationManager;
}
- @BinderThread
- public void onOverviewToggle() {
- // If currently screen pinning, do not enter overview
- if (mDeviceState.isScreenPinningActive()) {
+ /**
+ * Called when the command finishes execution.
+ */
+ private void scheduleNextTask(CommandInfo command) {
+ if (!mPendingCommands.isEmpty() && mPendingCommands.get(0) == command) {
+ mPendingCommands.remove(0);
+ executeNext();
+ }
+ }
+
+ /**
+ * Executes the next command from the queue. If the command finishes immediately (returns true),
+ * it continues to execute the next command, until the queue is empty of a command defer's its
+ * completion (returns false).
+ */
+ @UiThread
+ private void executeNext() {
+ if (mPendingCommands.isEmpty()) {
return;
}
-
- TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
+ CommandInfo cmd = mPendingCommands.get(0);
+ if (executeCommand(cmd)) {
+ scheduleNextTask(cmd);
+ }
}
+ @UiThread
+ private void addCommand(CommandInfo cmd) {
+ boolean wasEmpty = mPendingCommands.isEmpty();
+ mPendingCommands.add(cmd);
+ if (wasEmpty) {
+ executeNext();
+ }
+ }
+
+ /**
+ * Adds a command to be executed next, after all pending tasks are completed
+ */
@BinderThread
- public void onOverviewShown(boolean triggeredFromAltTab) {
- if (triggeredFromAltTab) {
- TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- }
- MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
+ public void addCommand(int type) {
+ CommandInfo cmd = new CommandInfo(type);
+ MAIN_EXECUTOR.execute(() -> addCommand(cmd));
}
- @BinderThread
- public void onOverviewHidden() {
- MAIN_EXECUTOR.execute(new HideRecentsCommand());
+ private TaskView getNextTask(RecentsView view) {
+ final TaskView runningTaskView = view.getRunningTaskView();
+
+ if (runningTaskView == null) {
+ return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
+ } else {
+ final TaskView nextTask = view.getNextTaskView();
+ return nextTask != null ? nextTask : runningTaskView;
+ }
}
- private class ShowRecentsCommand extends RecentsActivityCommand {
-
- private final boolean mTriggeredFromAltTab;
-
- ShowRecentsCommand(boolean triggeredFromAltTab) {
- mTriggeredFromAltTab = triggeredFromAltTab;
+ private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
+ RunnableList callbackList = null;
+ if (taskView != null) {
+ taskView.setEndQuickswitchCuj(true);
+ callbackList = taskView.launchTaskAnimated();
}
- @Override
- protected boolean handleCommand(long elapsedTime) {
- // TODO: Go to the next page if started from alt-tab.
- return mActivityInterface.getVisibleRecentsView() != null;
+ if (callbackList != null) {
+ callbackList.add(() -> scheduleNextTask(cmd));
+ return false;
+ } else {
+ recents.startHome();
+ return true;
}
+ }
- @Override
- protected void onTransitionComplete() {
- // TODO(b/138729100) This doesn't execute first time launcher is run
- if (mTriggeredFromAltTab) {
- RecentsView rv = mActivityInterface.getVisibleRecentsView();
- if (rv == null) {
- return;
+ /**
+ * Executes the task and returns true if next task can be executed. If false, then the next
+ * task is deferred until {@link #scheduleNextTask} is called
+ */
+ private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
+ BaseActivityInterface<?, T> activityInterface =
+ mOverviewComponentObserver.getActivityInterface();
+ RecentsView recents = activityInterface.getVisibleRecentsView();
+ if (recents == null) {
+ if (cmd.type == TYPE_HIDE) {
+ // already hidden
+ return true;
+ }
+ } else {
+ switch (cmd.type) {
+ case TYPE_SHOW:
+ // already visible
+ return true;
+ case TYPE_HIDE: {
+ int currentPage = recents.getNextPage();
+ TaskView tv = (currentPage >= 0 && currentPage < recents.getTaskViewCount())
+ ? (TaskView) recents.getPageAt(currentPage)
+ : null;
+ return launchTask(recents, tv, cmd);
}
+ case TYPE_TOGGLE:
+ return launchTask(recents, getNextTask(recents), cmd);
+ }
+ }
+ if (activityInterface.switchToRecentsIfVisible(() -> scheduleNextTask(cmd))) {
+ // If successfully switched, wait until animation finishes
+ return false;
+ }
+
+ final T activity = activityInterface.getCreatedActivity();
+ if (activity != null) {
+ InteractionJankMonitorWrapper.begin(
+ activity.getRootView(),
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ }
+
+ GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE);
+ AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
+ .newHandler(gestureState, cmd.createTime);
+ interactionHandler.setGestureEndCallback(
+ () -> onTransitionComplete(cmd, interactionHandler));
+
+ Intent intent = new Intent(interactionHandler.getLaunchIntent());
+ interactionHandler.initWhenReady(intent);
+
+ RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ interactionHandler.onGestureEnded(0, new PointF(), new PointF());
+ cmd.removeListener(this);
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ interactionHandler.onGestureCancelled();
+ cmd.removeListener(this);
+ }
+ };
+
+ if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
+ cmd.mActiveCallbacks.addListener(interactionHandler);
+ mTaskAnimationManager.notifyRecentsAnimationState(interactionHandler);
+ interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/);
+
+ cmd.mActiveCallbacks.addListener(recentAnimListener);
+ mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
+ } else {
+ intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
+ cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
+ gestureState, intent, interactionHandler);
+ interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
+ cmd.mActiveCallbacks.addListener(recentAnimListener);
+ }
+
+ Trace.beginAsyncSection(TRANSITION_NAME, 0);
+ return false;
+ }
+
+ private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
+ cmd.removeListener(handler);
+ Trace.endAsyncSection(TRANSITION_NAME, 0);
+
+ if (cmd.type == TYPE_SHOW_NEXT_FOCUS) {
+ RecentsView rv =
+ mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
+ if (rv != null) {
// Ensure that recents view has focus so that it receives the followup key inputs
TaskView taskView = rv.getNextTaskView();
if (taskView == null) {
@@ -120,130 +244,22 @@
}
}
}
+ scheduleNextTask(cmd);
}
- private class HideRecentsCommand extends RecentsActivityCommand {
+ private static class CommandInfo {
+ public final long createTime = SystemClock.elapsedRealtime();
+ public final int type;
+ RecentsAnimationCallbacks mActiveCallbacks;
- @Override
- protected boolean handleCommand(long elapsedTime) {
- RecentsView recents = mActivityInterface.getVisibleRecentsView();
- if (recents == null) {
- return false;
- }
- int currentPage = recents.getNextPage();
- if (currentPage >= 0 && currentPage < recents.getTaskViewCount()) {
- ((TaskView) recents.getPageAt(currentPage)).launchTaskAnimated();
- } else {
- recents.startHome();
- }
- return true;
- }
- }
-
- private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
-
- private static final String TRANSITION_NAME = "Transition:toOverview";
- protected final BaseActivityInterface<?, T> mActivityInterface;
- private final long mCreateTime;
- private final AppToOverviewAnimationProvider<T> mAnimationProvider;
-
- private final long mToggleClickedTime = SystemClock.uptimeMillis();
- private ActivityInitListener mListener;
-
- public RecentsActivityCommand() {
- mActivityInterface = mOverviewComponentObserver.getActivityInterface();
- mCreateTime = SystemClock.elapsedRealtime();
- mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
- ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
-
- // Preload the plan
- RecentsModel.INSTANCE.get(mContext).getTasks(null);
+ CommandInfo(int type) {
+ this.type = type;
}
- @Override
- public void run() {
- long elapsedTime = mCreateTime - mLastToggleTime;
- mLastToggleTime = mCreateTime;
-
- if (handleCommand(elapsedTime)) {
- // Command already handled.
- return;
+ void removeListener(RecentsAnimationListener listener) {
+ if (mActiveCallbacks != null) {
+ mActiveCallbacks.removeListener(listener);
}
-
- if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {
- // If successfully switched, then return
- return;
- }
-
- final T activity = mActivityInterface.getCreatedActivity();
- if (activity != null) {
- InteractionJankMonitorWrapper.begin(
- activity.getRootView(),
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
- }
-
- // Otherwise, start overview.
- mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
- mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
- new RemoteAnimationProvider() {
- @Override
- public AnimatorSet createWindowAnimation(
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
- return RecentsActivityCommand.this.createWindowAnimation(appTargets,
- wallpaperTargets);
- }
- }, mContext, MAIN_EXECUTOR.getHandler(),
- mAnimationProvider.getRecentsLaunchDuration());
}
-
- protected boolean handleCommand(long elapsedTime) {
- // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
- // the menu activity which takes window focus, preventing the right condition from
- // being run below
- RecentsView recents = mActivityInterface.getVisibleRecentsView();
- if (recents != null) {
- // Launch the next task
- recents.showNextTask();
- return true;
- } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
- // The user tried to launch back into overview too quickly, either after
- // launching an app, or before overview has actually shown, just ignore for now
- return true;
- }
- return false;
- }
-
- private boolean onActivityReady(Boolean wasVisible) {
- final T activity = mActivityInterface.getCreatedActivity();
- return mAnimationProvider.onActivityReady(activity, wasVisible);
- }
-
- private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
- LatencyTrackerCompat.logToggleRecents(
- mContext, (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
-
- mListener.unregister();
-
- AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(appTargets,
- wallpaperTargets);
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- Trace.beginAsyncSection(TRANSITION_NAME, 0);
- super.onAnimationStart(animation);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- onTransitionComplete();
- Trace.endAsyncSection(TRANSITION_NAME, 0);
- }
- });
- return animatorSet;
- }
-
- protected void onTransitionComplete() { }
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index ec585cc..82e8a93 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.graphics.Rect;
+import android.window.PictureInPictureSurfaceTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -149,14 +150,13 @@
* accordingly. This should be called before `finish`
* @param taskId for which the leash should be updated
* @param destinationBounds bounds of the final PiP window
- * @param windowCrop bounds to crop as part of final transform.
- * @param float9 An array of 9 floats to be used as matrix transform.
+ * @param finishTransaction leash operations for the final transform.
*/
- public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
- float[] float9) {
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds,
+ PictureInPictureSurfaceTransaction finishTransaction) {
UI_HELPER_EXECUTOR.execute(
- () -> mController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
- float9));
+ () -> mController.setFinishTaskBounds(taskId, destinationBounds,
+ finishTransaction));
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index f99b7e6..458f45a 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,12 +17,13 @@
import static android.content.Intent.ACTION_USER_UNLOCKED;
-import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
-import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -60,11 +61,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayHolder;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
import com.android.quickstep.util.NavBarPosition;
@@ -304,6 +305,13 @@
}
/**
+ * @return whether the current nav mode is 2-button-based.
+ */
+ public boolean isTwoButtonNavMode() {
+ return mMode == TWO_BUTTONS;
+ }
+
+ /**
* @return whether the current nav mode is button-based.
*/
public boolean isButtonNavMode() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index da0a664..718c5ba 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -41,13 +41,4 @@
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
-
- public boolean hasTask(int taskId) {
- for (RemoteAnimationTargetCompat target : unfilteredApps) {
- if (target.taskId == taskId) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a70cc4c..cf71eae 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -35,12 +35,17 @@
import android.view.MotionEvent;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.recents.ISplitScreenListener;
-import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.wm.shell.onehanded.IOneHanded;
+import com.android.wm.shell.pip.IPip;
+import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.splitscreen.ISplitScreen;
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import com.android.wm.shell.startingsurface.IStartingWindow;
+import com.android.wm.shell.startingsurface.IStartingWindowListener;
+import com.android.wm.shell.transition.IShellTransitions;
/**
* Holds the reference to SystemUI.
@@ -53,8 +58,13 @@
new MainThreadInitializedObject<>(SystemUiProxy::new);
private ISystemUiProxy mSystemUiProxy;
+ private IPip mPip;
+ private ISplitScreen mSplitScreen;
+ private IOneHanded mOneHanded;
+ private IShellTransitions mShellTransitions;
+ private IStartingWindow mStartingWindow;
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
- MAIN_EXECUTOR.execute(() -> setProxy(null));
+ MAIN_EXECUTOR.execute(() -> clearProxy());
};
// Used to dedupe calls to SystemUI
@@ -83,12 +93,23 @@
return null;
}
- public void setProxy(ISystemUiProxy proxy) {
+ public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
+ IOneHanded oneHanded, IShellTransitions shellTransitions,
+ IStartingWindow startingWindow) {
unlinkToDeath();
mSystemUiProxy = proxy;
+ mPip = pip;
+ mSplitScreen = splitScreen;
+ mOneHanded = oneHanded;
+ mShellTransitions = shellTransitions;
+ mStartingWindow = startingWindow;
linkToDeath();
}
+ public void clearProxy() {
+ setProxy(null, null, null, null, null, null);
+ }
+
// TODO(141886704): Find a way to remove this
public void setLastSystemUiStateFlags(int stateFlags) {
mLastSystemUiStateFlags = stateFlags;
@@ -268,21 +289,6 @@
}
@Override
- public void setShelfHeight(boolean visible, int shelfHeight) {
- boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
- if (mSystemUiProxy != null && changed) {
- mLastShelfVisible = visible;
- mLastShelfHeight = shelfHeight;
- try {
- mSystemUiProxy.setShelfHeight(visible, shelfHeight);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setShelfHeight visible: " + visible
- + " height: " + shelfHeight, e);
- }
- }
- }
-
- @Override
public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) {
if (mSystemUiProxy != null) {
try {
@@ -318,20 +324,6 @@
}
}
- /**
- * Sets listener to get pinned stack animation callbacks.
- */
- @Override
- public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.setPinnedStackAnimationListener(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
- }
- }
- }
-
@Override
public void onQuickSwitchToNewTask(int rotation) {
if (mSystemUiProxy != null) {
@@ -357,28 +349,6 @@
}
@Override
- public void startOneHandedMode() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.startOneHandedMode();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startOneHandedMode", e);
- }
- }
- }
-
- @Override
- public void stopOneHandedMode() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.stopOneHandedMode();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call stopOneHandedMode", e);
- }
- }
- }
-
- @Override
public void expandNotificationPanel() {
if (mSystemUiProxy != null) {
try {
@@ -389,12 +359,45 @@
}
}
- @Override
+ //
+ // Pip
+ //
+
+ /**
+ * Sets the shelf height.
+ */
+ public void setShelfHeight(boolean visible, int shelfHeight) {
+ boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
+ if (mPip != null && changed) {
+ mLastShelfVisible = visible;
+ mLastShelfHeight = shelfHeight;
+ try {
+ mPip.setShelfHeight(visible, shelfHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ + " height: " + shelfHeight, e);
+ }
+ }
+ }
+
+ /**
+ * Sets listener to get pinned stack animation callbacks.
+ */
+ public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
+ if (mPip != null) {
+ try {
+ mPip.setPinnedStackAnimationListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
+ }
+ }
+ }
+
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
- if (mSystemUiProxy != null) {
+ if (mPip != null) {
try {
- return mSystemUiProxy.startSwipePipToHome(componentName, activityInfo,
+ return mPip.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams, launcherRotation, shelfHeight);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startSwipePipToHome", e);
@@ -403,111 +406,85 @@
return null;
}
- @Override
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- if (mSystemUiProxy != null) {
+ if (mPip != null) {
try {
- mSystemUiProxy.stopSwipePipToHome(componentName, destinationBounds);
+ mPip.stopSwipePipToHome(componentName, destinationBounds);
} catch (RemoteException e) {
Log.w(TAG, "Failed call stopSwipePipToHome");
}
}
}
- @Override
- public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.registerRemoteTransition(remoteTransition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerRemoteTransition");
- }
- }
- }
+ //
+ // Splitscreen
+ //
- @Override
- public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.unregisterRemoteTransition(remoteTransition);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call registerRemoteTransition");
- }
- }
- }
-
- @Override
public void registerSplitScreenListener(ISplitScreenListener listener) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.registerSplitScreenListener(listener);
+ mSplitScreen.registerSplitScreenListener(listener);
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerSplitScreenListener");
}
}
}
- @Override
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.unregisterSplitScreenListener(listener);
+ mSplitScreen.unregisterSplitScreenListener(listener);
} catch (RemoteException e) {
Log.w(TAG, "Failed call unregisterSplitScreenListener");
}
}
}
- @Override
public void setSideStageVisibility(boolean visible) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.setSideStageVisibility(visible);
+ mSplitScreen.setSideStageVisibility(visible);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setSideStageVisibility");
}
}
}
- @Override
public void exitSplitScreen() {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.exitSplitScreen();
+ mSplitScreen.exitSplitScreen();
} catch (RemoteException e) {
Log.w(TAG, "Failed call exitSplitScreen");
}
}
}
- @Override
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ mSplitScreen.exitSplitScreenOnHide(exitSplitScreenOnHide);
} catch (RemoteException e) {
Log.w(TAG, "Failed call exitSplitScreen");
}
}
}
- @Override
public void startTask(int taskId, int stage, int position, Bundle options) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.startTask(taskId, stage, position, options);
+ mSplitScreen.startTask(taskId, stage, position, options);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTask");
}
}
}
- @Override
public void startShortcut(String packageName, String shortcutId, int stage, int position,
Bundle options, UserHandle user) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options,
+ mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
user);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcut");
@@ -515,38 +492,87 @@
}
}
- @Override
- public void startIntent(PendingIntent intent, Intent fillInIntent, int stage,
- int position, Bundle options) {
- if (mSystemUiProxy != null) {
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+ Bundle options) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.startIntent(intent, fillInIntent, stage, position,
- options);
+ mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntent");
}
}
}
- @Override
public void removeFromSideStage(int taskId) {
- if (mSystemUiProxy != null) {
+ if (mSplitScreen != null) {
try {
- mSystemUiProxy.removeFromSideStage(taskId);
+ mSplitScreen.removeFromSideStage(taskId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call removeFromSideStage");
}
}
}
+ //
+ // One handed
+ //
+
+ public void startOneHandedMode() {
+ if (mOneHanded != null) {
+ try {
+ mOneHanded.startOneHanded();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startOneHandedMode", e);
+ }
+ }
+ }
+
+ public void stopOneHandedMode() {
+ if (mOneHanded != null) {
+ try {
+ mOneHanded.stopOneHanded();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopOneHandedMode", e);
+ }
+ }
+ }
+
+ //
+ // Remote transitions
+ //
+
+ public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mShellTransitions != null) {
+ try {
+ mShellTransitions.registerRemote(remoteTransition.getFilter(),
+ remoteTransition.getTransition());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
+
+ public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mShellTransitions != null) {
+ try {
+ mShellTransitions.unregisterRemote(remoteTransition.getTransition());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
+
+ //
+ // Starting window
+ //
+
/**
* Sets listener to get callbacks when launching a task.
*/
- @Override
public void setStartingWindowListener(IStartingWindowListener listener) {
- if (mSystemUiProxy != null) {
+ if (mStartingWindow != null) {
try {
- mSystemUiProxy.setStartingWindowListener(listener);
+ mStartingWindow.setStartingWindowListener(listener);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setStartingWindowListener", e);
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 02c2763..b6dad2d 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -17,10 +17,11 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -31,13 +32,13 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;
-
-import java.util.function.Consumer;
+import com.android.systemui.shared.system.TaskStackChangeListener;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean ENABLE_SHELL_TRANSITIONS =
@@ -49,9 +50,24 @@
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
- private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
+ private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
+ private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
+ if (LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ && activityInterface.getCreatedActivity() != null) {
+ RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
+ recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ mLiveTileRestartListener);
+ }
+ }
+ };
+
TaskAnimationManager(Context ctx) {
mCtx = ctx;
}
@@ -113,9 +129,14 @@
@Override
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
- if (mLaunchOtherTaskHandler != null
- && mLastGestureState.getEndTarget() == RECENTS) {
- mLaunchOtherTaskHandler.accept(appearedTaskTarget);
+ BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
+ if (LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+ && activityInterface.getCreatedActivity() != null) {
+ RecentsView recentsView =
+ activityInterface.getCreatedActivity().getOverviewPanel();
+ RemoteAnimationTargetCompat[] apps = new RemoteAnimationTargetCompat[1];
+ apps[0] = appearedTaskTarget;
+ recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
return;
}
if (mController != null) {
@@ -160,13 +181,12 @@
return mCallbacks;
}
- /**
- * The passed-in handler is used to render side task launch animation in recents in live tile
- * mode.
- */
- public void setLaunchOtherTaskInLiveTileModeHandler(
- Consumer<RemoteAnimationTargetCompat> handler) {
- mLaunchOtherTaskHandler = handler;
+ public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) {
+ mLiveTileCleanUpHandler = cleanUpHandler;
+ }
+
+ public void enableLiveTileRestartListener() {
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mLiveTileRestartListener);
}
/**
@@ -206,6 +226,12 @@
* Cleans up the recents animation entirely.
*/
private void cleanUpRecentsAnimation() {
+ if (mLiveTileCleanUpHandler != null) {
+ mLiveTileCleanUpHandler.run();
+ mLiveTileCleanUpHandler = null;
+ }
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
+
// Release all the target leashes
if (mTargets != null) {
mTargets.release();
@@ -221,7 +247,6 @@
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTarget = null;
- mLaunchOtherTaskHandler = null;
}
public void dump() {
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index dd0ed8f..ba1c413 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import android.app.ActivityManager.TaskDescription;
@@ -34,7 +33,6 @@
import androidx.annotation.WorkerThread;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
@@ -137,21 +135,22 @@
// TODO: Load icon resource (b/143363444)
Bitmap icon = TaskDescriptionCompat.getIcon(desc, key.userId);
if (icon != null) {
- entry.icon = new FastBitmapDrawable(getBitmapInfo(
+ /* isInstantApp */
+ entry.icon = getBitmapInfo(
new BitmapDrawable(mContext.getResources(), icon),
key.userId,
desc.getPrimaryColor(),
- false /* isInstantApp */));
+ false /* isInstantApp */).newIcon(mContext);
} else {
activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
key.getComponent(), key.userId);
if (activityInfo != null) {
BitmapInfo bitmapInfo = getBitmapInfo(
- mIconProvider.getIcon(activityInfo, UserHandle.of(key.userId)),
+ mIconProvider.getIcon(activityInfo),
key.userId,
desc.getPrimaryColor(),
activityInfo.applicationInfo.isInstantApp());
- entry.icon = newIcon(mContext, bitmapInfo);
+ entry.icon = bitmapInfo.newIcon(mContext);
} else {
entry.icon = getDefaultIcon(key.userId);
}
@@ -199,7 +198,7 @@
}
mDefaultIcons.put(userId, info);
}
- return new FastBitmapDrawable(info);
+ return info.newIcon(mContext);
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cd13200..1ad5f2c 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -176,7 +176,7 @@
if (thumbnail != null) {
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
- boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 7a428ce..79db842 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
@@ -200,8 +201,7 @@
tsv.setPreview(targets.apps[targets.apps.length - 1]);
tsv.fullScreenProgress.value = 0;
tsv.recentsViewScale.value = 1;
- tsv.gridProgress.value = gridProgress;
- tsv.gridTranslationSecondary.value = gridTranslationSecondary;
+ tsv.taskSecondaryTranslation.value = gridTranslationSecondary;
tsv.setScroll(startScroll);
// Fade in the task during the initial 20% of the animation
@@ -214,7 +214,8 @@
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
out.setFloat(tsv.recentsViewScale,
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
- out.setFloat(tsv.gridProgress, AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(tsv.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
+ TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL);
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
TaskViewSimulator finalTsv = tsv;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index fc805d0..1fb9465 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,6 +24,12 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -101,6 +107,11 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.wm.shell.onehanded.IOneHanded;
+import com.android.wm.shell.pip.IPip;
+import com.android.wm.shell.splitscreen.ISplitScreen;
+import com.android.wm.shell.startingsurface.IStartingWindow;
+import com.android.wm.shell.transition.IShellTransitions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -140,8 +151,18 @@
public void onInitialize(Bundle bundle) {
ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+ IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
+ ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
+ KEY_EXTRA_SHELL_SPLIT_SCREEN));
+ IOneHanded onehanded = IOneHanded.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
+ IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
+ IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
MAIN_EXECUTOR.execute(() -> {
- SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
+ SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
+ splitscreen, onehanded, shellTransitions, startingWindow);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
mDeviceState.runOnUserUnlocked(() -> {
@@ -157,13 +178,23 @@
@BinderThread
public void onOverviewToggle() {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
- mOverviewCommandHelper.onOverviewToggle();
+ // If currently screen pinning, do not enter overview
+ if (mDeviceState.isScreenPinningActive()) {
+ return;
+ }
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
}
@BinderThread
@Override
public void onOverviewShown(boolean triggeredFromAltTab) {
- mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+ if (triggeredFromAltTab) {
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW_NEXT_FOCUS);
+ } else {
+ mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
+ }
}
@BinderThread
@@ -171,7 +202,7 @@
public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
if (triggeredFromAltTab && !triggeredFromHomeKey) {
// onOverviewShownFromAltTab hides the overview and ends at the target app
- mOverviewCommandHelper.onOverviewHidden();
+ mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
}
}
@@ -326,8 +357,8 @@
public void onUserUnlocked() {
mTaskAnimationManager = new TaskAnimationManager(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
- mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
- mOverviewComponentObserver);
+ mOverviewCommandHelper = new OverviewCommandHelper(this,
+ mOverviewComponentObserver, mTaskAnimationManager);
mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
@@ -421,7 +452,7 @@
}
disposeEventHandlers();
mDeviceState.destroy();
- SystemUiProxy.INSTANCE.get(this).setProxy(null);
+ SystemUiProxy.INSTANCE.get(this).clearProxy();
ProtoTracer.INSTANCE.get(this).stop();
ProtoTracer.INSTANCE.get(this).remove(this);
@@ -555,7 +586,7 @@
ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
- private GestureState createGestureState(GestureState previousGestureState) {
+ public GestureState createGestureState(GestureState previousGestureState) {
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
@@ -704,16 +735,15 @@
}
}
+ public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
+ return !mOverviewComponentObserver.isHomeAndOverviewSame()
+ ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
+ }
+
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
MotionEvent event) {
- final AbsSwipeUpHandler.Factory factory;
- if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
- factory = mFallbackSwipeHandlerFactory;
- } else {
- factory = mLauncherSwipeHandlerFactory;
- }
-
+ final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory();
final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
|| gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
@@ -857,6 +887,9 @@
if (mGestureState != null) {
mGestureState.dump(pw);
}
+ pw.println("Input state:");
+ pw.println(" mInputMonitorCompat=" + mInputMonitorCompat);
+ pw.println(" mInputEventReceiver=" + mInputEventReceiver);
SysUINavigationMode.INSTANCE.get(this).dump(pw);
pw.println("TouchState:");
BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
@@ -886,15 +919,17 @@
}
private AbsSwipeUpHandler createLauncherSwipeHandler(
- GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+ GestureState gestureState, long touchTimeMs) {
return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
- gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+ gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ mInputConsumer);
}
private AbsSwipeUpHandler createFallbackSwipeHandler(
- GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+ GestureState gestureState, long touchTimeMs) {
return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
- gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+ gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ mInputConsumer);
}
protected boolean shouldNotifyBackGesture() {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 24a7610..03a0b4e 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -20,14 +20,14 @@
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.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
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.FULLSCREEN_PROGRESS;
+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_MODALNESS;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
+import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
@@ -61,15 +61,11 @@
@Override
public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config,
PendingAnimation setter) {
- if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
- // The entire recents animation is played atomically.
- return;
- }
if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return;
}
// While animating into recents, update the visible task data as needed
- setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+ setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
setProperties(toState, config, setter);
@@ -77,11 +73,12 @@
private void setProperties(RecentsState state, StateAnimationConfig config,
PropertySetter setter) {
- float buttonAlpha = state.hasButtons() ? 1 : 0;
+ float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
- buttonAlpha, LINEAR);
+ clearAllButtonAlpha, LINEAR);
+ float overviewButtonAlpha = state.hasOverviewActions(mActivity) ? 1 : 0;
setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
+ MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
@@ -94,5 +91,7 @@
setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
+ setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
+ state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()) ? 1f : 0f, LINEAR);
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 02fd5bb..e075045 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -66,6 +66,7 @@
@Override
public void startHome() {
mActivity.startHome();
+ mActivity.getStateManager().goToState(RecentsState.HOME);
}
/**
@@ -155,6 +156,11 @@
}
@Override
+ protected boolean isHomeTask(TaskView taskView) {
+ return mHomeTaskInfo != null && taskView.hasTaskId(mHomeTaskInfo.taskId);
+ }
+
+ @Override
public void setModalStateEnabled(boolean isModalState) {
super.setModalStateEnabled(isModalState);
if (isModalState) {
@@ -169,6 +175,8 @@
@Override
public void onStateTransitionStart(RecentsState toState) {
setOverviewStateEnabled(true);
+ setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+ setOverviewFullscreenEnabled(toState.isFullScreen());
setFreezeViewVisibility(true);
}
@@ -183,7 +191,7 @@
super.setOverviewStateEnabled(enabled);
if (enabled) {
RecentsState state = mActivity.getStateManager().getState();
- setDisallowScrollToClearAll(!state.hasButtons());
+ setDisallowScrollToClearAll(!state.hasClearAllButton());
}
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index f15a9de..a9856d2 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -20,6 +20,8 @@
import android.content.Context;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.BaseState;
import com.android.quickstep.RecentsActivity;
@@ -29,14 +31,19 @@
public class RecentsState implements BaseState<RecentsState> {
private static final int FLAG_MODAL = BaseState.getFlag(0);
- private static final int FLAG_HAS_BUTTONS = BaseState.getFlag(1);
+ private static final int FLAG_CLEAR_ALL_BUTTON = BaseState.getFlag(1);
private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2);
+ private static final int FLAG_OVERVIEW_ACTIONS = BaseState.getFlag(3);
+ private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
- public static final RecentsState DEFAULT = new RecentsState(0, FLAG_HAS_BUTTONS);
+ public static final RecentsState DEFAULT = new RecentsState(0,
+ FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID);
public static final RecentsState MODAL_TASK = new ModalState(1,
- FLAG_DISABLE_RESTORE | FLAG_HAS_BUTTONS | FLAG_MODAL);
+ FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
+ | FLAG_SHOW_AS_GRID);
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 final int ordinal;
private final int mFlags;
@@ -82,14 +89,35 @@
return hasFlag(FLAG_FULL_SCREEN);
}
- public boolean hasButtons() {
- return hasFlag(FLAG_HAS_BUTTONS);
+ /**
+ * For this state, whether clear all button should be shown.
+ */
+ public boolean hasClearAllButton() {
+ return hasFlag(FLAG_CLEAR_ALL_BUTTON);
+ }
+
+ /**
+ * For this state, whether overview actions should be shown.
+ */
+ public boolean hasOverviewActions(RecentsActivity activity) {
+ return hasFlag(FLAG_OVERVIEW_ACTIONS) && !showAsGrid(activity.getDeviceProfile());
}
public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
return new float[] { NO_SCALE, NO_OFFSET };
}
+ /**
+ * For this state, whether tasks should layout as a grid rather than a list.
+ */
+ public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
+ return hasFlag(FLAG_SHOW_AS_GRID) && showAsGrid(deviceProfile);
+ }
+
+ private boolean showAsGrid(DeviceProfile deviceProfile) {
+ return deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+ }
+
private static class ModalState extends RecentsState {
public ModalState(int id, int flags) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 5baf518..9878d45 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -31,7 +31,6 @@
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.annotation.TargetApi;
import android.content.Context;
@@ -65,7 +64,6 @@
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
@@ -381,9 +379,6 @@
// Once we detect the gesture, we can enable batching to reduce further updates
mInputEventReceiver.setBatchingEnabled(true);
- mActivityInterface.closeOverlay();
- TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-
// Notify the handler that the gesture has actually started
mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
}
@@ -391,8 +386,7 @@
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
- mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
- mTaskAnimationManager.isRecentsAnimationRunning());
+ mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index cee3363..fa9e0ec 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -24,6 +24,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
@@ -38,7 +39,7 @@
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
-public class OverviewInputConsumer<T extends StatefulActivity<?>>
+public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulActivity<S>>
implements InputConsumer {
private final T mActivity;
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 161e015..a1b7e01 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -36,9 +36,8 @@
Integer getTitleStringId() {
switch (mTutorialType) {
case RIGHT_EDGE_BACK_NAVIGATION:
- return R.string.back_gesture_tutorial_playground_title_swipe_inward_right_edge;
case LEFT_EDGE_BACK_NAVIGATION:
- return R.string.back_gesture_tutorial_playground_title_swipe_inward_left_edge;
+ return R.string.back_gesture_intro_title;
case BACK_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
@@ -49,9 +48,8 @@
Integer getSubtitleStringId() {
switch (mTutorialType) {
case RIGHT_EDGE_BACK_NAVIGATION:
- return R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_right_edge;
case LEFT_EDGE_BACK_NAVIGATION:
- return R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_left_edge;
+ return R.string.back_gesture_intro_subtitle;
case BACK_NAVIGATION_COMPLETE:
return R.string.back_gesture_tutorial_confirm_subtitle;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 95352d1..fbf3a0a 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -38,7 +38,7 @@
Integer getTitleStringId() {
switch (mTutorialType) {
case HOME_NAVIGATION:
- return R.string.home_gesture_tutorial_playground_title;
+ return R.string.home_gesture_intro_title;
case HOME_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
@@ -48,7 +48,7 @@
@Override
Integer getSubtitleStringId() {
if (mTutorialType == TutorialType.HOME_NAVIGATION) {
- return R.string.home_gesture_tutorial_playground_subtitle;
+ return R.string.home_gesture_intro_subtitle;
}
return null;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 45cbd0b..31f26d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -39,7 +39,7 @@
Integer getTitleStringId() {
switch (mTutorialType) {
case OVERVIEW_NAVIGATION:
- return R.string.overview_gesture_tutorial_playground_title;
+ return R.string.overview_gesture_intro_title;
case OVERVIEW_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
@@ -49,7 +49,7 @@
@Override
Integer getSubtitleStringId() {
if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) {
- return R.string.overview_gesture_tutorial_playground_subtitle;
+ return R.string.overview_gesture_intro_subtitle;
}
return null;
}
diff --git a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
new file mode 100644
index 0000000..3730284
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util;
+
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.IAssistDataReceiver;
+import android.app.assist.AssistContent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.launcher3.util.Executors;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ */
+public class AssistContentRequester {
+ private static final String TAG = "AssistContentRequester";
+ private static final String ASSIST_KEY_CONTENT = "content";
+
+ /** For receiving content, called on the main thread. */
+ public interface Callback {
+ /**
+ * Called when the {@link android.app.assist.AssistContent} of the requested task is
+ * available.
+ **/
+ void onAssistContentAvailable(AssistContent assistContent);
+ }
+
+ private final IActivityTaskManager mActivityTaskManager;
+ private final String mPackageName;
+ private final Executor mCallbackExecutor;
+
+ public AssistContentRequester(Context context) {
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageName = context.getApplicationContext().getPackageName();
+ mCallbackExecutor = Executors.MAIN_EXECUTOR;
+ }
+
+ /**
+ * Request the {@link AssistContent} from the task with the provided id.
+ *
+ * @param taskId to query for the content.
+ * @param callback to call when the content is available, called on the main thread.
+ */
+ public void requestAssistContent(int taskId, Callback callback) {
+ try {
+ mActivityTaskManager.requestAssistDataForTask(
+ new AssistDataReceiver(callback, mCallbackExecutor), taskId, mPackageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
+ }
+ }
+
+ private static final class AssistDataReceiver extends IAssistDataReceiver.Stub {
+
+ private final Executor mExecutor;
+ private final Callback mCallback;
+
+ AssistDataReceiver(Callback callback, Executor callbackExecutor) {
+ mCallback = callback;
+ mExecutor = callbackExecutor;
+ }
+
+ @Override
+ public void onHandleAssistData(Bundle data) {
+ if (data == null) {
+ return;
+ }
+
+ final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
+ if (content == null) {
+ Log.e(TAG, "Received AssistData, but no AssistContent found");
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onAssistContentAvailable(content));
+ }
+
+ @Override
+ public void onHandleAssistScreenshot(Bitmap screenshot) {}
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index 3e87f48..2e5b33a 100644
--- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -38,7 +38,8 @@
private static final String TAG = "InputConsumerProxy";
private final InputConsumerController mInputConsumerController;
- private final Supplier<InputConsumer> mConsumerSupplier;
+ private Runnable mCallback;
+ private Supplier<InputConsumer> mConsumerSupplier;
// The consumer is created lazily on demand.
private InputConsumer mInputConsumer;
@@ -48,8 +49,9 @@
private boolean mDestroyPending = false;
public InputConsumerProxy(InputConsumerController inputConsumerController,
- Supplier<InputConsumer> consumerSupplier) {
+ Runnable callback, Supplier<InputConsumer> consumerSupplier) {
mInputConsumerController = inputConsumerController;
+ mCallback = callback;
mConsumerSupplier = consumerSupplier;
}
@@ -64,9 +66,7 @@
if (ev instanceof MotionEvent) {
onInputConsumerMotionEvent((MotionEvent) ev);
} else if (ev instanceof KeyEvent) {
- if (mInputConsumer == null) {
- mInputConsumer = mConsumerSupplier.get();
- }
+ initInputConsumerIfNeeded();
mInputConsumer.onKeyEvent((KeyEvent) ev);
return true;
}
@@ -89,9 +89,7 @@
if (action == ACTION_DOWN) {
mTouchInProgress = true;
- if (mInputConsumer == null) {
- mInputConsumer = mConsumerSupplier.get();
- }
+ initInputConsumerIfNeeded();
} else if (action == ACTION_CANCEL || action == ACTION_UP) {
// Finish any pending actions
mTouchInProgress = false;
@@ -115,4 +113,18 @@
mDestroyed = true;
mInputConsumerController.setInputListener(null);
}
+
+ public void unregisterCallback() {
+ mCallback = null;
+ }
+
+ private void initInputConsumerIfNeeded() {
+ if (mInputConsumer == null) {
+ if (mCallback != null) {
+ mCallback.run();
+ }
+ mInputConsumer = mConsumerSupplier.get();
+ mConsumerSupplier = null;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
new file mode 100644
index 0000000..8209c09
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.inputconsumers.OverviewInputConsumer;
+
+import java.util.function.Supplier;
+
+/**
+ * A factory that creates a input consumer for
+ * {@link com.android.quickstep.util.InputConsumerProxy}.
+ */
+public class InputProxyHandlerFactory implements Supplier<InputConsumer> {
+
+ private final BaseActivityInterface mActivityInterface;
+ private final GestureState mGestureState;
+
+ @UiThread
+ public InputProxyHandlerFactory(BaseActivityInterface activityInterface,
+ GestureState gestureState) {
+ mActivityInterface = activityInterface;
+ mGestureState = gestureState;
+ }
+
+ /**
+ * Called to create a input proxy for the running task
+ */
+ @Override
+ public InputConsumer get() {
+ StatefulActivity activity = mActivityInterface.getCreatedActivity();
+ return activity == null ? InputConsumer.NO_OP
+ : new OverviewInputConsumer(mGestureState, activity, null, true);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
index 60c7add..351adf4 100644
--- a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
+++ b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
@@ -20,6 +20,8 @@
import android.content.Context;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SysUINavigationMode;
import java.util.function.Predicate;
@@ -35,6 +37,7 @@
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) {
@@ -43,12 +46,16 @@
}
public boolean get() {
- return mBasePredicate.get() && mSupported;
+ return mBasePredicate.get() && mSupported && mObserver.isHomeAndOverviewSame();
}
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 1031c5b..1128dac 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -22,13 +22,11 @@
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
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.PLAY_ATOMIC_OVERVIEW_PEEK;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -105,10 +103,6 @@
StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
config.duration = duration;
- config.animFlags = playStaggeredWorkspaceAnim
- // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
- ? PLAY_ATOMIC_OVERVIEW_PEEK
- : ANIM_ALL_COMPONENTS;
boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
.isLayoutNaturalToLauncher();
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 932ff27..6757e4c 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -21,10 +21,8 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
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_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,6 +35,7 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -60,7 +59,10 @@
public class StaggeredWorkspaceAnim {
private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
+ // How long it takes to fade in each staggered row.
private static final int ALPHA_DURATION_MS = 250;
+ // Should be used for animations running alongside this StaggeredWorkspaceAnim.
+ public static final int DURATION_MS = 250;
private static final float MAX_VELOCITY_PX_PER_S = 22f;
@@ -82,85 +84,106 @@
DeviceProfile grid = launcher.getDeviceProfile();
Workspace workspace = launcher.getWorkspace();
- CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
- ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
- ViewGroup hotseat = launcher.getHotseat();
+ Hotseat hotseat = launcher.getHotseat();
+
+ // Hotseat and QSB takes up two additional rows.
+ int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
+
+ // Add animation for all the visible workspace pages
+ workspace.getVisiblePages()
+ .forEach(page -> addAnimationForPage((CellLayout) page, totalRows));
boolean workspaceClipChildren = workspace.getClipChildren();
boolean workspaceClipToPadding = workspace.getClipToPadding();
- boolean cellLayoutClipChildren = cellLayout.getClipChildren();
- boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
boolean hotseatClipChildren = hotseat.getClipChildren();
boolean hotseatClipToPadding = hotseat.getClipToPadding();
workspace.setClipChildren(false);
workspace.setClipToPadding(false);
- cellLayout.setClipChildren(false);
- cellLayout.setClipToPadding(false);
hotseat.setClipChildren(false);
hotseat.setClipToPadding(false);
- // Hotseat and QSB takes up two additional rows.
- int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
-
- // Set up springs on workspace items.
- for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
- View child = currentPage.getChildAt(i);
- CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
- addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
- }
-
// Set up springs for the hotseat and qsb.
- ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
+ ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
if (grid.isVerticalBarLayout()) {
- for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
- View child = hotseatChild.getChildAt(i);
+ for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+ View child = hotseatIcons.getChildAt(i);
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
}
} else {
- for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
- View child = hotseatChild.getChildAt(i);
- addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
+ final int hotseatRow, qsbRow, taskbarRow;
+ if (grid.isTaskbarPresent) {
+ qsbRow = grid.inv.numRows + 1;
+ hotseatRow = grid.inv.numRows + 2;
+ } else {
+ hotseatRow = grid.inv.numRows + 1;
+ qsbRow = grid.inv.numRows + 2;
+ }
+ // Taskbar and hotseat overlap.
+ taskbarRow = hotseatRow;
+
+ for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+ View child = hotseatIcons.getChildAt(i);
+ addStaggeredAnimationForView(child, hotseatRow, totalRows);
}
- if (launcher.getAppsView().getSearchUiManager()
- .isQsbVisible(NORMAL.getVisibleElements(launcher))) {
- addStaggeredAnimationForView(launcher.getAppsView().getSearchView(),
- grid.inv.numRows + 2, totalRows);
- }
+ addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
+ addStaggeredAnimationForView(hotseat.getTaskbarView(), taskbarRow, totalRows);
}
if (animateOverviewScrim) {
- PendingAnimation pendingAnimation = new PendingAnimation(ALPHA_DURATION_MS);
+ PendingAnimation pendingAnimation = new PendingAnimation(DURATION_MS);
addScrimAnimationForState(launcher, NORMAL, pendingAnimation);
mAnimators.play(pendingAnimation.buildAnim());
}
- addDepthAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+ addDepthAnimationForState(launcher, NORMAL, DURATION_MS);
mAnimators.play(launcher.getDragLayer().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)
- .setDuration(ALPHA_DURATION_MS));
+ .setDuration(DURATION_MS));
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
workspace.setClipChildren(workspaceClipChildren);
workspace.setClipToPadding(workspaceClipToPadding);
- cellLayout.setClipChildren(cellLayoutClipChildren);
- cellLayout.setClipToPadding(cellLayoutClipToPadding);
hotseat.setClipChildren(hotseatClipChildren);
hotseat.setClipToPadding(hotseatClipToPadding);
}
});
}
+ private void addAnimationForPage(CellLayout page, int totalRows) {
+ ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets();
+
+ boolean pageClipChildren = page.getClipChildren();
+ boolean pageClipToPadding = page.getClipToPadding();
+
+ page.setClipChildren(false);
+ page.setClipToPadding(false);
+
+ // Set up springs on workspace items.
+ for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
+ View child = itemsContainer.getChildAt(i);
+ CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+ addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
+ }
+
+ mAnimators.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ page.setClipChildren(pageClipChildren);
+ page.setClipToPadding(pageClipToPadding);
+ }
+ });
+ }
+
/**
* Setup workspace with 0 duration to prepare for our staggered animation.
*/
private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
StateAnimationConfig config = new StateAnimationConfig();
- config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER
- | SKIP_TASKBAR;
+ config.animFlags = SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
config.duration = 0;
// setRecentsAttachedToAppWindow() will animate recents out.
launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 0a1a6e8..a1240e0 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -29,6 +29,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
+import android.window.PictureInPictureSurfaceTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -46,10 +47,12 @@
* Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
* PiP window, which would ideally be on SysUI side as well.
*/
-public class SwipePipToHomeAnimator extends ValueAnimator implements
- ValueAnimator.AnimatorUpdateListener {
+public class SwipePipToHomeAnimator extends ValueAnimator {
private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+ public static final float FRACTION_START = 0f;
+ public static final float FRACTION_END = 1f;
+
private final int mTaskId;
private final ComponentName mComponentName;
private final SurfaceControl mLeash;
@@ -139,7 +142,7 @@
mHasAnimationEnded = true;
}
});
- addUpdateListener(this);
+ addUpdateListener(this::onAnimationUpdate);
}
/** sets the from rotation if it's different from the target rotation. */
@@ -167,48 +170,53 @@
mAppBounds.top + mDestinationBounds.height());
}
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
+ private void onAnimationUpdate(ValueAnimator animator) {
if (mHasAnimationEnded) return;
-
- final float fraction = animator.getAnimatedFraction();
- final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
- mDestinationBoundsAnimation);
final SurfaceControl.Transaction tx =
PipSurfaceTransactionHelper.newSurfaceControlTransaction();
- if (mSourceHintRectInsets == null) {
- // no source rect hint been set, directly scale the window down
- onAnimationScale(fraction, tx, bounds);
- } else {
- // scale and crop according to the source rect hint
- onAnimationScaleAndCrop(fraction, tx, bounds);
- }
- mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
+ onAnimationUpdate(tx, animator.getAnimatedFraction());
tx.apply();
}
+ private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx,
+ float fraction) {
+ final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
+ mDestinationBoundsAnimation);
+ final PictureInPictureSurfaceTransaction op;
+ if (mSourceHintRectInsets == null) {
+ // no source rect hint been set, directly scale the window down
+ op = onAnimationScale(fraction, tx, bounds);
+ } else {
+ // scale and crop according to the source rect hint
+ op = onAnimationScaleAndCrop(fraction, tx, bounds);
+ }
+ return op;
+ }
+
/** scale the window directly with no source rect hint being set */
- private void onAnimationScale(float fraction, SurfaceControl.Transaction tx, Rect bounds) {
+ private PictureInPictureSurfaceTransaction onAnimationScale(
+ float fraction, SurfaceControl.Transaction tx, Rect bounds) {
if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
- mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
+ return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
} else {
- mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
+ return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
}
}
/** scale and crop the window with source rect hint */
- private void onAnimationScaleAndCrop(float fraction, SurfaceControl.Transaction tx,
+ private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop(
+ float fraction, SurfaceControl.Transaction tx,
Rect bounds) {
final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
mSourceHintRectInsets);
if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
- mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+ return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
} else {
- mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+ return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
}
}
@@ -224,34 +232,12 @@
return mDestinationBounds;
}
- /**
- * @return {@link Rect} of the final window crop in destination orientation.
- */
- public Rect getFinishWindowCrop() {
- final Rect windowCrop = new Rect(mAppBounds);
- if (mSourceHintRectInsets != null) {
- windowCrop.inset(mSourceHintRectInsets);
- }
- return windowCrop;
- }
-
- /**
- * @return Array of 9 floats represents the final transform in destination orientation.
- */
- public float[] getFinishTransform() {
- final Matrix transform = new Matrix();
- final float[] float9 = new float[9];
- if (mSourceHintRectInsets == null) {
- transform.setRectToRect(new RectF(mAppBounds), new RectF(mDestinationBounds),
- Matrix.ScaleToFit.FILL);
- } else {
- final float scale = mAppBounds.width() <= mAppBounds.height()
- ? (float) mDestinationBounds.width() / mAppBounds.width()
- : (float) mDestinationBounds.height() / mAppBounds.height();
- transform.setScale(scale, scale);
- }
- transform.getValues(float9);
- return float9;
+ /** @return {@link PictureInPictureSurfaceTransaction} for the final leash transaction. */
+ public PictureInPictureSurfaceTransaction getFinishTransaction() {
+ // get the final leash operations but do not apply to the leash.
+ final SurfaceControl.Transaction tx =
+ PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+ return onAnimationUpdate(tx, FRACTION_END);
}
private RotatedPosition getRotatedPosition(float fraction) {
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 6e8a5f1..6cfe302 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -36,7 +35,6 @@
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -79,7 +77,6 @@
private final boolean mIsRecentsRtl;
private final Rect mTaskRect = new Rect();
- private final Rect mGridRect = new Rect();
private boolean mDrawsBelowRecents;
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
@@ -98,22 +95,18 @@
private final FullscreenDrawParams mCurrentFullscreenParams;
public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
- public final AnimatedFloat gridTranslationSecondary = new AnimatedFloat();
// RecentsView properties
public final AnimatedFloat recentsViewScale = new AnimatedFloat();
public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
public final AnimatedFloat recentsViewPrimaryTranslation = new AnimatedFloat();
- public final AnimatedFloat gridProgress = new AnimatedFloat();
private final ScrollState mScrollState = new ScrollState();
// Cached calculations
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
private int mOrientationStateId;
- private final int mTaskThumbnailPadding;
- private final int mRowSpacing;
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
@@ -125,8 +118,6 @@
mOrientationStateId = mOrientationState.getStateId();
Resources resources = context.getResources();
mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources);
- mTaskThumbnailPadding = (int) resources.getDimension(R.dimen.task_thumbnail_top_margin);
- mRowSpacing = (int) resources.getDimension(R.dimen.overview_grid_row_spacing);
}
/**
@@ -268,13 +259,14 @@
mOrientationStateId = mOrientationState.getStateId();
getFullScreenScale();
- mSizeStrategy.calculateGridSize(mContext, mDp, mGridRect);
mThumbnailData.rotation = mOrientationState.getDisplayRotation();
+ // mIsRecentsRtl is the inverse of TaskView RTL.
+ boolean isRtlEnabled = !mIsRecentsRtl;
mPositionHelper.updateThumbnailMatrix(
mThumbnailPosition, mThumbnailData,
mTaskRect.width(), mTaskRect.height(),
- mDp, mOrientationState.getRecentsActivityRotation());
+ mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
@@ -307,34 +299,6 @@
mMatrix.postTranslate(insets.left, insets.top);
mMatrix.postScale(scale, scale);
- // Apply TaskView matrix: gridProgress related properties
- float interpolatedGridProgress = ACCEL_DEACCEL.getInterpolation(gridProgress.value);
- final int boxLength = (int) Math.max(taskWidth, taskHeight);
- float availableHeight = mGridRect.height();
- float rowHeight = (availableHeight - mRowSpacing) / 2;
- float gridScale = rowHeight / (boxLength + mTaskThumbnailPadding);
- scale = Utilities.mapRange(interpolatedGridProgress, 1f, gridScale);
- mMatrix.postScale(scale, scale, mIsRecentsRtl ? 0 : taskWidth, 0);
- mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
- Utilities.mapRange(interpolatedGridProgress, 0, gridTranslationSecondary.value));
-
- // Apply TaskView matrix: task rect and grid rect difference
- float scaledWidth = taskWidth * gridScale;
- float taskGridHorizontalDiff;
- if (mIsRecentsRtl) {
- float taskRight = mTaskRect.left + scaledWidth;
- taskGridHorizontalDiff = mGridRect.right - taskRight;
- } else {
- float taskLeft = mTaskRect.right - scaledWidth;
- taskGridHorizontalDiff = mGridRect.left - taskLeft;
- }
- float taskGridVerticalDiff =
- mGridRect.top + mTaskThumbnailPadding * gridScale - mTaskRect.top;
- mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
- Utilities.mapRange(interpolatedGridProgress, 0, taskGridHorizontalDiff));
- mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
- Utilities.mapRange(interpolatedGridProgress, 0, taskGridVerticalDiff));
-
// Apply TaskView matrix: translate, scroll
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index e7101cc..ff78995 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -21,6 +21,7 @@
import android.util.FloatProperty;
import android.widget.Button;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.views.RecentsView.PageCallbacks;
import com.android.quickstep.views.RecentsView.ScrollState;
@@ -40,30 +41,32 @@
}
};
+ private final StatefulActivity mActivity;
private float mScrollAlpha = 1;
private float mContentAlpha = 1;
private float mVisibilityAlpha = 1;
private float mGridProgress = 1;
private boolean mIsRtl;
- private final float mOriginalTranslationX, mOriginalTranslationY;
private float mNormalTranslationPrimary;
private float mGridTranslationPrimary;
+ private float mGridTranslationSecondary;
+ private float mGridScrollOffset;
+ private float mScrollOffsetPrimary;
- private int mScrollOffset;
+ private int mSidePadding;
public ClearAllButton(Context context, AttributeSet attrs) {
super(context, attrs);
mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- mOriginalTranslationX = getTranslationX();
- mOriginalTranslationY = getTranslationY();
+ mActivity = StatefulActivity.fromContext(context);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
- mScrollOffset = orientationHandler.getClearAllScrollOffset(getRecentsView(), mIsRtl);
+ mSidePadding = orientationHandler.getClearAllSidePadding(getRecentsView(), mIsRtl);
}
private RecentsView getRecentsView() {
@@ -96,25 +99,27 @@
}
@Override
- public void onPageScroll(ScrollState scrollState) {
- PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
+ public void onPageScroll(ScrollState scrollState, boolean gridEnabled) {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null) {
+ return;
+ }
+
+ PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
if (orientationSize == 0) {
return;
}
- float shift;
- if (mIsRtl) {
- shift = Math.min(scrollState.scrollFromEdge, orientationSize);
- } else {
- shift = Math.min(scrollState.scrollFromEdge,
- orientationSize + getGridTrans(mGridTranslationPrimary))
- - getGridTrans(mGridTranslationPrimary);
+ int leftEdgeScroll = recentsView.getLeftMostChildScroll();
+ float adjustedScrollFromEdge = scrollState.scrollFromEdge - leftEdgeScroll;
+ float shift = Math.min(adjustedScrollFromEdge, orientationSize);
+ mNormalTranslationPrimary = mIsRtl ? -shift : shift;
+ if (!gridEnabled) {
+ mNormalTranslationPrimary += mSidePadding;
}
- mNormalTranslationPrimary = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift);
applyPrimaryTranslation();
- orientationHandler.getSecondaryViewTranslate().set(this,
- orientationHandler.getSecondaryValue(mOriginalTranslationX, mOriginalTranslationY));
+ applySecondaryTranslation();
mScrollAlpha = 1 - shift / orientationSize;
updateAlpha();
}
@@ -130,11 +135,25 @@
applyPrimaryTranslation();
}
+ public void setGridTranslationSecondary(float gridTranslationSecondary) {
+ mGridTranslationSecondary = gridTranslationSecondary;
+ applyPrimaryTranslation();
+ }
+
+ public void setGridScrollOffset(float gridScrollOffset) {
+ mGridScrollOffset = gridScrollOffset;
+ }
+
+ public void setScrollOffsetPrimary(float scrollOffsetPrimary) {
+ mScrollOffsetPrimary = scrollOffsetPrimary;
+ }
+
public float getScrollAdjustment(boolean gridEnabled) {
float scrollAdjustment = 0;
if (gridEnabled) {
- scrollAdjustment += mGridTranslationPrimary;
+ scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
}
+ scrollAdjustment += mScrollOffsetPrimary;
return scrollAdjustment;
}
@@ -160,10 +179,30 @@
PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
orientationHandler.getPrimaryViewTranslate().set(this,
- mNormalTranslationPrimary + getGridTrans(mGridTranslationPrimary));
+ orientationHandler.getPrimaryValue(0f, getOriginalTranslationY())
+ + mNormalTranslationPrimary + getGridTrans(mGridTranslationPrimary));
+ }
+
+ private void applySecondaryTranslation() {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null) {
+ return;
+ }
+
+ PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+ orientationHandler.getSecondaryViewTranslate().set(this,
+ orientationHandler.getSecondaryValue(0f, getOriginalTranslationY())
+ + getGridTrans(mGridTranslationSecondary));
}
private float getGridTrans(float endTranslation) {
return mGridProgress > 0 ? endTranslation : 0;
}
+
+ /**
+ * Get the Y translation that is set in the original layout position, before scrolling.
+ */
+ private float getOriginalTranslationY() {
+ return mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx / 2.0f;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
index 7cc00b7..ed642df 100644
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -21,26 +21,14 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.FastBitmapDrawable;
-
-import java.util.ArrayList;
-
/**
* A view which draws a drawable stretched to fit its size. Unlike ImageView, it avoids relayout
* when the drawable changes.
*/
public class IconView extends View {
- public interface OnScaleUpdateListener {
- public void onScaleUpdate(float scale);
- }
-
private Drawable mDrawable;
- private ArrayList<OnScaleUpdateListener> mScaleListeners;
-
public IconView(Context context) {
super(context);
}
@@ -94,16 +82,6 @@
}
@Override
- public void invalidateDrawable(@NonNull Drawable drawable) {
- super.invalidateDrawable(drawable);
- if (drawable instanceof FastBitmapDrawable && mScaleListeners != null) {
- for (OnScaleUpdateListener listener : mScaleListeners) {
- listener.onScaleUpdate(((FastBitmapDrawable) drawable).getScale());
- }
- }
- }
-
- @Override
protected void onDraw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
@@ -115,22 +93,6 @@
return false;
}
- public void addUpdateScaleListener(OnScaleUpdateListener listener) {
- if (mScaleListeners == null) {
- mScaleListeners = new ArrayList<>();
- }
- mScaleListeners.add(listener);
- if (mDrawable instanceof FastBitmapDrawable) {
- listener.onScaleUpdate(((FastBitmapDrawable) mDrawable).getScale());
- }
- }
-
- public void removeUpdateScaleListener(OnScaleUpdateListener listener) {
- if (mScaleListeners != null) {
- mScaleListeners.remove(listener);
- }
- }
-
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 9d31190..a0af68a 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,18 +15,13 @@
*/
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.QuickstepTransitionManager.ALL_APPS_PROGRESS_OFF_SCREEN;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
@@ -41,7 +36,6 @@
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
@@ -104,35 +98,10 @@
}
}
- /**
- * Animates adjacent tasks and translate hotseat off screen as well.
- */
- @Override
- public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
- AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
-
- if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
- // Hotseat doesn't move when opening recents with the button,
- // so don't animate it here either.
- return anim;
- }
-
- float allAppsProgressOffscreen = ALL_APPS_PROGRESS_OFF_SCREEN;
- LauncherState state = mActivity.getStateManager().getState();
- if ((state.getVisibleElements(mActivity) & ALL_APPS_HEADER_EXTRA) != 0) {
- float maxShiftRange = mActivity.getDeviceProfile().heightPx;
- float currShiftRange = mActivity.getAllAppsController().getShiftRange();
- allAppsProgressOffscreen = 1f + (maxShiftRange - currShiftRange) / maxShiftRange;
- }
- anim.play(ObjectAnimator.ofFloat(
- mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
- return anim;
- }
-
@Override
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
- mActivity.getStateManager().goToState(NORMAL, false /* animate */);
+ mActivity.getStateManager().moveToRestState();
} else {
LauncherState state = mActivity.getStateManager().getState();
mActivity.getAllAppsController().setState(state);
@@ -150,6 +119,8 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
setOverviewStateEnabled(toState.overviewUi);
+ setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+ setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
setFreezeViewVisibility(true);
}
@@ -160,8 +131,6 @@
reset();
}
setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
- setOverviewGridEnabled(finalState.displayOverviewTasksAsGrid(mActivity));
- setOverviewFullscreenEnabled(finalState.getOverviewFullscreenProgress() == 1);
setFreezeViewVisibility(false);
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1241982..6fcd54c 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -29,6 +29,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.util.MultiValueAlpha;
@@ -144,6 +145,7 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+ updateHorizontalPadding();
}
public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
@@ -187,6 +189,10 @@
return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
}
+ private void updateHorizontalPadding() {
+ setPadding(mInsets.left, 0, mInsets.right, 0);
+ }
+
/** Updates vertical margins for different navigation mode or configuration changes. */
public void updateVerticalMargin(Mode mode) {
LayoutParams actionParams = (LayoutParams) findViewById(
@@ -196,6 +202,13 @@
getBottomVerticalMargin(mode));
}
+ /**
+ * Set the device profile for this view to draw with.
+ */
+ public void setDp(DeviceProfile dp) {
+ requestLayout();
+ }
+
protected int getBottomVerticalMargin(Mode mode) {
int bottomMargin;
int orientation = getResources().getConfiguration().orientation;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f5b62d5..ad266ce 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
@@ -34,12 +35,14 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
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.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
@@ -49,6 +52,7 @@
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
@@ -60,6 +64,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -128,21 +133,26 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.wm.shell.pip.IPipAnimationListener;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -300,6 +310,7 @@
protected final Rect mTempRect = new Rect();
protected final RectF mTempRectF = new RectF();
private final PointF mTempPointF = new PointF();
+ private float mFullscreenScale;
private static final int DISMISS_TASK_DURATION = 300;
private static final int ADDITION_TASK_DURATION = 200;
@@ -309,11 +320,14 @@
protected final T mActivity;
private final float mFastFlingVelocity;
private final RecentsModel mModel;
- private final int mTaskTopMargin;
private final int mRowSpacing;
private final ClearAllButton mClearAllButton;
private final Rect mClearAllButtonDeadZoneRect = new Rect();
private final Rect mTaskViewDeadZoneRect = new Rect();
+ /**
+ * Reflects if Recents is currently in the middle of a gesture
+ */
+ private boolean mGestureActive;
private final ScrollState mScrollState = new ScrollState();
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
@@ -337,7 +351,11 @@
private float mTaskViewsPrimaryTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0;
- private boolean mShowAsGrid;
+
+ // The GestureEndTarget that is still in progress.
+ private GestureState.GestureEndTarget mCurrentGestureEndTarget;
+
+ IntSet mTopIdSet = new IntSet();
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -407,7 +425,7 @@
}
};
- private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
+ private final PinnedStackAnimationListener mIPipAnimationListener =
new PinnedStackAnimationListener();
// Used to keep track of the last requested task list id, so that we do not request to load the
@@ -522,8 +540,6 @@
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
- mTaskTopMargin = getResources()
- .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
mRowSpacing = getResources().getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
mSquaredTouchSlop = squaredTouchSlop(context);
@@ -554,11 +570,6 @@
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
mLiveTileTaskViewSimulator.setOrientationState(mOrientationState);
mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
-
- mShowAsGrid =
- mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
- mActivity.addOnDeviceProfileChangeListener(newDp ->
- mShowAsGrid = newDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
}
public OverScroller getScroller() {
@@ -617,9 +628,6 @@
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
- if (visibility != VISIBLE && LIVE_TILE.get()) {
- finishRecentsAnimation(true /* toRecents */, null);
- }
updateTaskStackListenerState();
}
@@ -629,8 +637,8 @@
return;
}
mModel.getIconCache().clear();
- unloadVisibleTaskData();
- loadVisibleTaskData();
+ unloadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
+ loadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
}
public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
@@ -659,9 +667,9 @@
mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIdp.addOnChangeListener(this);
- mIPinnedStackAnimationListener.setActivity(mActivity);
+ mIPipAnimationListener.setActivity(mActivity);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
- mIPinnedStackAnimationListener);
+ mIPipAnimationListener);
mOrientationState.initListeners();
SplitScreenBounds.INSTANCE.addOnChangeListener(this);
mTaskOverlayFactory.initListeners();
@@ -680,7 +688,7 @@
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
- mIPinnedStackAnimationListener.setActivity(null);
+ mIPipAnimationListener.setActivity(null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
}
@@ -718,6 +726,72 @@
super.draw(canvas);
}
+ public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
+ if (mRunningTaskId != -1 && mRunningTaskId == taskId &&
+ getLiveTileParams().getTargetSet().findTask(taskId) != null) {
+ launchSideTaskInLiveTileMode(taskId, getLiveTileParams().getTargetSet().apps);
+ }
+ }
+
+ public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps) {
+ AnimatorSet anim = new AnimatorSet();
+ TaskView taskView = getTaskView(taskId);
+ if (taskView == null || !isTaskViewVisible(taskView)) {
+ // TODO: Refine this animation.
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(mActivity.getDragLayer());
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+ appAnimator.setInterpolator(ACCEL_DEACCEL);
+ appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ @Override
+ public void onUpdate(float percent) {
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(
+ apps[apps.length - 1].leash);
+ Matrix matrix = new Matrix();
+ matrix.postScale(percent, percent);
+ matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
+ mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
+ builder.withAlpha(percent).withMatrix(matrix);
+ surfaceApplier.scheduleApply(builder.build());
+ }
+ });
+ anim.play(appAnimator);
+ } else {
+ TaskViewUtils.composeRecentsLaunchAnimator(
+ anim, taskView, apps,
+ mLiveTileParams.getTargetSet().wallpapers, true /* launcherClosing */,
+ mActivity.getStateManager(), this,
+ getDepthController());
+ }
+ anim.addListener(new AnimatorListenerAdapter(){
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ cleanUp(false);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ cleanUp(true);
+ }
+
+ private void cleanUp(boolean canceled) {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */,
+ null /* onFinishComplete */);
+ if (canceled) {
+ mRecentsAnimationController = null;
+ } else {
+ mSizeStrategy.onLaunchTaskSuccess();
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+ }
+ });
+ anim.start();
+ }
+
private void updateTaskStartIndex(View affectingView) {
if (!(affectingView instanceof TaskView) && !(affectingView instanceof ClearAllButton)) {
int childCount = getChildCount();
@@ -731,7 +805,7 @@
}
public boolean isTaskViewVisible(TaskView tv) {
- if (mShowAsGrid) {
+ if (showAsGrid()) {
int screenStart = mOrientationHandler.getPrimaryScroll(this);
int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
return isTaskViewWithinBounds(tv, screenStart, screenEnd);
@@ -743,9 +817,9 @@
private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
- mOverviewFullscreenEnabled, mOverviewGridEnabled);
+ mOverviewFullscreenEnabled, showAsGrid());
int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment(
- mOverviewFullscreenEnabled, mOverviewGridEnabled));
+ mOverviewFullscreenEnabled));
int taskEnd = taskStart + taskSize;
return (taskStart >= start && taskStart <= end) || (taskEnd >= start
&& taskEnd <= end);
@@ -813,7 +887,7 @@
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
- if (mShowAsGrid) {
+ if (showAsGrid()) {
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
TaskView taskView = getTaskViewAt(i);
@@ -880,7 +954,7 @@
@Override
protected boolean snapToPageInFreeScroll() {
- return !mShowAsGrid;
+ return !showAsGrid();
}
@Override
@@ -913,7 +987,7 @@
}
// Unload existing visible task data
- unloadVisibleTaskData();
+ unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
@@ -1035,7 +1109,7 @@
updateCurveProperties();
// Update the set of visible task's data
- loadVisibleTaskData();
+ loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setTaskModalness(0);
}
@@ -1066,7 +1140,9 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
resetPaddingFromTaskSize();
- mLiveTileTaskViewSimulator.setDp(mActivity.getDeviceProfile());
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ mLiveTileTaskViewSimulator.setDp(dp);
+ mActionsView.setDp(dp);
}
private void resetPaddingFromTaskSize() {
@@ -1075,7 +1151,7 @@
mTaskWidth = mTempRect.width();
mTaskHeight = mTempRect.height();
- mTempRect.top -= mTaskTopMargin;
+ mTempRect.top -= dp.overviewTaskThumbnailTopMarginPx;
setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
dp.widthPx - mInsets.right - mTempRect.right,
dp.heightPx - mInsets.bottom - mTempRect.bottom);
@@ -1091,15 +1167,47 @@
* Updates TaskView scaling and translation required to support variable width.
*/
private void updateTaskSize() {
- float accumulatedTranslationX = 0;
final int taskCount = getTaskViewCount();
+ if (taskCount == 0) {
+ return;
+ }
+
+ float accumulatedTranslationX = 0;
+ float[] fullscreenTranslations = new float[taskCount];
+ int firstNonHomeTaskIndex = 0;
for (int i = 0; i < taskCount; i++) {
TaskView taskView = getTaskViewAt(i);
+ if (isHomeTask(taskView)) {
+ if (firstNonHomeTaskIndex == i) {
+ firstNonHomeTaskIndex++;
+ }
+ continue;
+ }
+
taskView.updateTaskSize();
- taskView.setAccumulatedFullscreenTranslationX(accumulatedTranslationX);
- accumulatedTranslationX += taskView.getFullscreenTranslationX();
+ fullscreenTranslations[i] += accumulatedTranslationX;
+ // Compensate space caused by TaskView scaling.
+ float widthDiff =
+ taskView.getLayoutParams().width * (1 - taskView.getFullscreenScale());
+ // Compensate page spacing widening caused by RecentsView scaling.
+ widthDiff += mPageSpacing * (1 - 1 / mFullscreenScale);
+ float fullscreenTranslationX = mIsRtl ? widthDiff : -widthDiff;
+ accumulatedTranslationX += fullscreenTranslationX;
}
- updateGridProperties();
+
+ // We need to maintain first non-home task's full screen translation at 0, now shift
+ // translation of all the TaskViews to achieve that.
+ for (int i = firstNonHomeTaskIndex; i < taskCount; i++) {
+ getTaskViewAt(i).setFullscreenTranslationX(
+ fullscreenTranslations[i] - fullscreenTranslations[firstNonHomeTaskIndex]);
+ }
+
+ // Align ClearAllButton to the left (RTL) or right (non-RTL), which is different from other
+ // TaskViews.
+ int clearAllWidthDiff = mTaskWidth - mClearAllButton.getWidth();
+ mClearAllButton.setScrollOffsetPrimary(mIsRtl ? clearAllWidthDiff : -clearAllWidthDiff);
+
+ updateGridProperties(false);
}
public void getTaskSize(Rect outRect) {
@@ -1130,7 +1238,7 @@
}
// After scrolling, update the visible task's data
- loadVisibleTaskData();
+ loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
}
// Update the high res thumbnail loader state
@@ -1160,13 +1268,13 @@
View page = getPageAt(i);
mScrollState.updateInterpolation(mActivity.getDeviceProfile(),
mOrientationHandler.getChildStartWithTranslation(page));
- ((PageCallbacks) page).onPageScroll(mScrollState);
+ ((PageCallbacks) page).onPageScroll(mScrollState, mOverviewGridEnabled);
}
}
@Override
protected int getDestinationPage(int scaledScroll) {
- if (mGridProgress == 0) {
+ if (!showAsGrid()) {
return super.getDestinationPage(scaledScroll);
}
@@ -1193,7 +1301,7 @@
* Iterates through all the tasks, and loads the associated task data for newly visible tasks,
* and unloads the associated task data for tasks that are no longer visible.
*/
- public void loadVisibleTaskData() {
+ public void loadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
if (!mOverviewStateEnabled || mTaskListChangeId == -1) {
// Skip loading visible task data if we've already left the overview state, or if the
// task list hasn't been loaded yet (the task views will not reflect the task list)
@@ -1204,7 +1312,7 @@
int upper = 0;
int visibleStart = 0;
int visibleEnd = 0;
- if (mShowAsGrid) {
+ if (showAsGrid()) {
int screenStart = mOrientationHandler.getPrimaryScroll(this);
int pageOrientedSize = mOrientationHandler.getMeasuredSize(this);
int halfScreenSize = pageOrientedSize / 2;
@@ -1224,7 +1332,7 @@
Task task = taskView.getTask();
int index = indexOfChild(taskView);
boolean visible;
- if (mShowAsGrid) {
+ if (showAsGrid()) {
visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd);
} else {
visible = lower <= index && index <= upper;
@@ -1235,12 +1343,18 @@
continue;
}
if (!mHasVisibleTaskData.get(task.key.id)) {
- taskView.onTaskListVisibilityChanged(true /* visible */);
+ // Ignore thumbnail update if it's current running task during the gesture
+ // We snapshot at end of gesture, it will update then
+ int changes = dataChanges;
+ if (taskView == getRunningTaskView() && mGestureActive) {
+ changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
+ }
+ taskView.onTaskListVisibilityChanged(true /* visible */, changes);
}
mHasVisibleTaskData.put(task.key.id, visible);
} else {
if (mHasVisibleTaskData.get(task.key.id)) {
- taskView.onTaskListVisibilityChanged(false /* visible */);
+ taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
}
mHasVisibleTaskData.delete(task.key.id);
}
@@ -1250,12 +1364,12 @@
/**
* Unloads any associated data from the currently visible tasks
*/
- private void unloadVisibleTaskData() {
+ private void unloadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
if (mHasVisibleTaskData.valueAt(i)) {
TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
if (taskView != null) {
- taskView.onTaskListVisibilityChanged(false /* visible */);
+ taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
}
}
}
@@ -1293,7 +1407,7 @@
mRecentsAnimationController = null;
mLiveTileParams.setTargetSet(null);
- unloadVisibleTaskData();
+ unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
setCurrentPage(0);
mDwbToastShown = false;
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
@@ -1341,6 +1455,7 @@
* Called when a gesture from an app is starting.
*/
public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
+ mGestureActive = true;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
updateOrientationHandler();
@@ -1404,13 +1519,14 @@
* Called when a gesture from an app has finished, and an end target has been determined.
*/
public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
-
+ mCurrentGestureEndTarget = endTarget;
}
/**
* Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
+ mGestureActive = false;
if (mOrientationState.setGestureActive(false)) {
updateOrientationHandler();
}
@@ -1425,9 +1541,11 @@
animateUpRunningTaskIconScale();
// TODO: This should be tied to whether there is a focus app on overview.
- if (!mShowAsGrid) {
+ if (!showAsGrid()) {
animateActionsViewIn();
}
+
+ mCurrentGestureEndTarget = null;
}
/**
@@ -1518,29 +1636,6 @@
}
}
- public void showNextTask() {
- final TaskView runningTaskView = getRunningTaskView();
- final TaskView targetTask;
-
- if (runningTaskView == null) {
- // Launch the first task
- if (getTaskViewCount() > 0) {
- targetTask = getTaskViewAt(0);
- } else {
- return;
- }
- } else {
- final TaskView nextTask = getNextTaskView();
- if (nextTask != null) {
- targetTask = nextTask;
- } else {
- targetTask = runningTaskView;
- }
- }
- targetTask.setEndQuickswitchCuj(true);
- targetTask.launchTaskAnimated();
- }
-
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
if (mRunningTaskIconScaledDown != isScaledDown) {
mRunningTaskIconScaledDown = isScaledDown;
@@ -1585,63 +1680,50 @@
* This method only calculates the potential position and depends on {@link #setGridProgress} to
* apply the actual scaling and translation.
*/
- private void updateGridProperties() {
+ private void updateGridProperties(boolean isTaskDismissal) {
int taskCount = getTaskViewCount();
if (taskCount == 0) {
return;
}
final int boxLength = Math.max(mTaskWidth, mTaskHeight);
- float availableHeight = mLastComputedGridSize.height();
- float rowHeight = (availableHeight - mRowSpacing) / 2;
- float gridScale = rowHeight / (boxLength + mTaskTopMargin);
-
+ int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
int topRowWidth = 0;
int bottomRowWidth = 0;
float topAccumulatedTranslationX = 0;
float bottomAccumulatedTranslationX = 0;
IntSet topSet = new IntSet();
+ IntSet bottomSet = new IntSet();
float[] gridTranslations = new float[taskCount];
+ int firstNonHomeTaskIndex = 0;
+
+ if (!isTaskDismissal) {
+ mTopIdSet.clear();
+ }
for (int i = 0; i < taskCount; i++) {
TaskView taskView = getTaskViewAt(i);
- taskView.setGridScale(gridScale);
- gridTranslations[i] = 0;
-
- float scaledWidth = taskView.getLayoutParams().width * gridScale;
- float taskGridHorizontalDiff;
- if (mIsRtl) {
- float taskRight = mLastComputedTaskSize.left + scaledWidth;
- taskGridHorizontalDiff = mLastComputedGridSize.right - taskRight;
- } else {
- float taskLeft = mLastComputedTaskSize.right - scaledWidth;
- taskGridHorizontalDiff = mLastComputedGridSize.left - taskLeft;
+ if (isHomeTask(taskView)) {
+ if (firstNonHomeTaskIndex == i) {
+ firstNonHomeTaskIndex++;
+ }
+ continue;
}
- gridTranslations[i] -= taskGridHorizontalDiff;
- taskView.setGridOffsetTranslationX(taskGridHorizontalDiff);
- float taskGridVerticalDiff = mLastComputedGridSize.top + mTaskTopMargin * gridScale
- - mLastComputedTaskSize.top;
-
- // Off-set gap due to task scaling.
- float widthDiff = taskView.getLayoutParams().width * (1 - gridScale);
- float gridScaleTranslationX = mIsRtl ? widthDiff : -widthDiff;
- gridTranslations[i] += gridScaleTranslationX;
-
- // Visible offset caused by having scaling pivot on top-right.
- taskView.setNonRtlVisibleOffset(mIsRtl ? 0 : widthDiff);
-
- if (topRowWidth <= bottomRowWidth) {
+ // Evenly distribute tasks between rows unless rearranging due to task dismissal, in
+ // which case keep tasks in their respective rows.
+ if ((!isTaskDismissal && topRowWidth <= bottomRowWidth) || (isTaskDismissal && mTopIdSet
+ .contains(taskView.getTask().key.id))) {
gridTranslations[i] += topAccumulatedTranslationX;
- topRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing;
+ topRowWidth += taskView.getLayoutParams().width + mPageSpacing;
topSet.add(i);
+ mTopIdSet.add(taskView.getTask().key.id);
- taskView.setGridTranslationY(taskGridVerticalDiff);
+ taskView.setGridTranslationY(0);
// Move horizontally into empty space.
float widthOffset = 0;
- for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
- widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale
- + mPageSpacing;
+ for (int j = i - 1; bottomSet.contains(j); j--) {
+ widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -1649,25 +1731,31 @@
topAccumulatedTranslationX += gridTranslationX;
} else {
gridTranslations[i] += bottomAccumulatedTranslationX;
- bottomRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing;
+ bottomRowWidth += taskView.getLayoutParams().width + mPageSpacing;
+ bottomSet.add(i);
// Move into bottom row.
- float heightOffset = (boxLength + mTaskTopMargin) * gridScale + mRowSpacing;
- taskView.setGridTranslationY(heightOffset + taskGridVerticalDiff);
+ float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
+ taskView.setGridTranslationY(heightOffset);
// Move horizontally into empty space.
float widthOffset = 0;
for (int j = i - 1; topSet.contains(j); j--) {
- widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale
- + mPageSpacing;
+ widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
gridTranslations[i] += gridTranslationX;
bottomAccumulatedTranslationX += gridTranslationX;
}
- topAccumulatedTranslationX += gridScaleTranslationX;
- bottomAccumulatedTranslationX += gridScaleTranslationX;
+ }
+
+ // We need to maintain first non-home task's grid translation at 0, now shift translation
+ // of all the TaskViews to achieve that.
+ for (int i = firstNonHomeTaskIndex; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ taskView.setGridTranslationX(
+ gridTranslations[i] - gridTranslations[firstNonHomeTaskIndex]);
}
// Use the accumulated translation of the longer row.
@@ -1683,7 +1771,7 @@
shorterRowCompensation = bottomRowWidth - topRowWidth;
}
} else {
- if (!topSet.contains(taskCount - 1)) {
+ if (bottomSet.contains(taskCount - 1)) {
shorterRowCompensation = topRowWidth - bottomRowWidth;
}
}
@@ -1704,31 +1792,32 @@
clearAllAccumulatedTranslation + clearAllShorterRowCompensation
+ clearAllShortTotalCompensation;
- // We need to maintain first task's grid translation at 0, now shift translation of all
- // the TaskViews to achieve that.
- for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setGridTranslationX(gridTranslations[i] - gridTranslations[0]);
- }
- mClearAllButton.setGridTranslationPrimary(clearAllTotalTranslationX - gridTranslations[0]);
+ mClearAllButton.setGridTranslationPrimary(
+ clearAllTotalTranslationX - gridTranslations[firstNonHomeTaskIndex]);
+ mClearAllButton.setGridTranslationSecondary(
+ boxLength - mTaskHeight / 2f + mRowSpacing / 2f);
+ mClearAllButton.setGridScrollOffset(
+ mIsRtl ? mLastComputedTaskSize.left - mLastComputedGridSize.left
+ : mLastComputedTaskSize.right - mLastComputedGridSize.right);
setGridProgress(mGridProgress);
}
+ protected boolean isHomeTask(TaskView taskView) {
+ return false;
+ }
+
/**
* Moves TaskView and ClearAllButton between carousel and 2 row grid.
*
* @param gridProgress 0 = carousel; 1 = 2 row grid.
*/
- public void setGridProgress(float gridProgress) {
+ private void setGridProgress(float gridProgress) {
int taskCount = getTaskViewCount();
if (taskCount == 0) {
return;
}
- if (!mShowAsGrid) {
- gridProgress = 0;
- }
-
mGridProgress = gridProgress;
for (int i = 0; i < taskCount; i++) {
@@ -1781,8 +1870,10 @@
/**
* Updates the page UI based on scroll params.
+ *
+ * @param gridEnabled whether Overveiw is currently showing as 2 rows grid
*/
- default void onPageScroll(ScrollState scrollState) {}
+ default void onPageScroll(ScrollState scrollState, boolean gridEnabled) {}
}
public static class ScrollState extends CurveProperties {
@@ -1872,7 +1963,8 @@
if (animateTaskView) {
addDismissedTaskAnimations(taskView, duration, anim);
}
- } else if (!mShowAsGrid) { // Don't animate other tasks when dismissing in grid for now
+ } else if (!showAsGrid()) {
+ // For grid layout, don't animate other tasks when dismissing in grid for now.
// If we just take newScroll - oldScroll, everything to the right of dragged task
// translates to the left. We need to offset this in some cases:
// - In RTL, add page offset to all pages, since we want pages to move to the right
@@ -1979,7 +2071,7 @@
} else {
snapToPageImmediately(pageToSnapTo);
// Grid got messed up, reapply.
- updateGridProperties();
+ updateGridProperties(true);
}
// Update the layout synchronously so that the position of next view is
// immediately available.
@@ -2276,7 +2368,7 @@
// Update the pivots such that when the task is scaled, it fills the full page
getTaskSize(mTempRect);
- getPagedViewOrientedState().getFullScreenScaleAndPivot(
+ mFullscreenScale = getPagedViewOrientedState().getFullScreenScaleAndPivot(
mTempRect, mActivity.getDeviceProfile(), mTempPointF);
setPivotX(mTempPointF.x);
setPivotY(mTempPointF.y);
@@ -2464,7 +2556,7 @@
if (child == mSplitHiddenTaskView) {
int left = newScroll[i] + getPaddingStart();
- int topMargin = mSplitHiddenTaskView.getThumbnailTopMargin();
+ int topMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1];
mSplitHiddenTaskView.layout(left, top,
left + mSplitHiddenTaskView.getWidth(),
@@ -2724,7 +2816,7 @@
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
- loadVisibleTaskData();
+ loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
updateEnabledOverlays();
}
@@ -2837,22 +2929,42 @@
@Override
protected int computeMinScroll() {
if (getTaskViewCount() > 0) {
- if (mDisallowScrollToClearAll) {
+ if (mIsRtl && mDisallowScrollToClearAll) {
// We aren't showing the clear all button,
// so use the leftmost task as the min scroll.
- if (mIsRtl) {
- return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
- }
- return getScrollForPage(mTaskViewStartIndex);
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
}
- if (mIsRtl) {
- return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
- }
- return getScrollForPage(mTaskViewStartIndex);
+ return getLeftMostChildScroll();
}
return super.computeMinScroll();
}
+ /**
+ * Returns page scroll of the left most child.
+ */
+ public int getLeftMostChildScroll() {
+ if (mIsRtl) {
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
+ }
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+
+ @Override
+ protected int computeMaxScroll() {
+ if (getTaskViewCount() > 0) {
+ if (!mIsRtl && mDisallowScrollToClearAll) {
+ // We aren't showing the clear all button,
+ // so use the rightmost task as the min scroll.
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
+ }
+ if (mIsRtl) {
+ return getScrollForPage(mTaskViewStartIndex);
+ }
+ return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
+ }
+ return super.computeMaxScroll();
+ }
+
@Override
protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
ComputePageScrollsLogic scrollLogic) {
@@ -2865,9 +2977,9 @@
float scrollDiff = 0;
if (child instanceof TaskView) {
scrollDiff = ((TaskView) child).getScrollAdjustment(mOverviewFullscreenEnabled,
- mOverviewGridEnabled);
+ showAsGrid());
} else if (child instanceof ClearAllButton) {
- scrollDiff = ((ClearAllButton) child).getScrollAdjustment(mOverviewGridEnabled);
+ scrollDiff = ((ClearAllButton) child).getScrollAdjustment(showAsGrid());
}
if (scrollDiff != 0) {
@@ -2884,9 +2996,9 @@
View child = getChildAt(index);
if (child instanceof TaskView) {
childOffset += ((TaskView) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
- mOverviewGridEnabled);
+ showAsGrid());
} else if (child instanceof ClearAllButton) {
- childOffset += ((ClearAllButton) child).getOffsetAdjustment(mOverviewGridEnabled);
+ childOffset += ((ClearAllButton) child).getOffsetAdjustment(showAsGrid());
}
return childOffset;
}
@@ -2898,26 +3010,7 @@
return super.getChildVisibleSize(index);
}
return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment(
- mOverviewFullscreenEnabled, mOverviewGridEnabled));
- }
-
- @Override
- protected int computeMaxScroll() {
- if (getTaskViewCount() > 0) {
- if (mDisallowScrollToClearAll) {
- // We aren't showing the clear all button,
- // so use the rightmost task as the min scroll.
- if (mIsRtl) {
- return getScrollForPage(mTaskViewStartIndex);
- }
- return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
- }
- if (mIsRtl) {
- return getScrollForPage(mTaskViewStartIndex);
- }
- return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
- }
- return super.computeMaxScroll();
+ mOverviewFullscreenEnabled));
}
public ClearAllButton getClearAllButton() {
@@ -3109,6 +3202,12 @@
return mSizeStrategy;
}
+ private boolean showAsGrid() {
+ return mOverviewGridEnabled || (mCurrentGestureEndTarget != null
+ && mSizeStrategy.stateFromGestureEndTarget(
+ mCurrentGestureEndTarget).displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+ }
+
/**
* Used to register callbacks for when our empty message state changes.
*
@@ -3121,7 +3220,7 @@
}
private static class PinnedStackAnimationListener<T extends BaseActivity> extends
- IPinnedStackAnimationListener.Stub {
+ IPipAnimationListener.Stub {
private T mActivity;
public void setActivity(T activity) {
@@ -3129,10 +3228,14 @@
}
@Override
- public void onPinnedStackAnimationStarted() {
- // Needed for activities that auto-enter PiP, which will not trigger a remote
- // animation to be created
- mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+ public void onPipAnimationStarted() {
+ MAIN_EXECUTOR.execute(() -> {
+ // Needed for activities that auto-enter PiP, which will not trigger a remote
+ // animation to be created
+ if (mActivity != null) {
+ mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+ }
+ });
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index a5b7a5b..a46daf3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -58,7 +58,6 @@
private static final int REVEAL_OPEN_DURATION = 150;
private static final int REVEAL_CLOSE_DURATION = 100;
- private final float mThumbnailTopMargin;
private BaseDraggingActivity mActivity;
private TextView mTaskName;
private AnimatorSet mOpenCloseAnimator;
@@ -73,7 +72,6 @@
super(context, attrs, defStyleAttr);
mActivity = BaseDraggingActivity.fromContext(context);
- mThumbnailTopMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
setClipToOutline(true);
}
@@ -123,14 +121,15 @@
}
public void setPosition(float x, float y, PagedOrientationHandler pagedOrientationHandler) {
- float adjustedY = y + mThumbnailTopMargin;
+ int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+ float adjustedY = y + taskTopMargin;
// Changing pivot to make computations easier
// NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
// which would render the X and Y position set here incorrect
setPivotX(0);
if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
// In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
- setPivotY(-mThumbnailTopMargin);
+ setPivotY(-taskTopMargin);
} else {
setPivotY(0);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 36a5f03..af62582 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -379,9 +379,10 @@
mThumbnailData.thumbnail.getHeight());
int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
.getRecentsActivityRotation();
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
- currentRotation);
+ currentRotation, isRtl);
mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
mPaint.setShader(mBitmapShader);
@@ -439,13 +440,14 @@
}
/**
- * Returns whether the snapshot is real.
+ * Returns whether the snapshot is real. If the device is locked for the user of the task,
+ * the snapshot used will be an app-theme generated snapshot instead of a real snapshot.
*/
public boolean isRealSnapshot() {
if (mThumbnailData == null) {
return false;
}
- return mThumbnailData.isRealSnapshot;
+ return mThumbnailData.isRealSnapshot && !mTask.isLocked;
}
/**
@@ -466,7 +468,8 @@
* Updates the matrix based on the provided parameters
*/
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
- int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation) {
+ int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation,
+ boolean isRtl) {
boolean isRotated = false;
boolean isOrientationDifferent;
@@ -500,6 +503,17 @@
float availableHeight = surfaceHeight
- (thumbnailClipHint.top + thumbnailClipHint.bottom);
+ if (isRotated) {
+ float canvasAspect = canvasWidth / (float) canvasHeight;
+ float availableAspect = availableHeight / availableWidth;
+ // Do not rotate thumbnail if it would not improve fit
+ if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
+ availableAspect, 0.1f)) {
+ isRotated = false;
+ isOrientationDifferent = false;
+ }
+ }
+
final float targetW, targetH;
if (isOrientationDifferent) {
targetW = canvasHeight;
@@ -535,21 +549,21 @@
}
}
- // Update the clip hints
- float halfExtraW = (availableWidth - croppedWidth) / 2;
- thumbnailClipHint.left += halfExtraW;
- thumbnailClipHint.right += halfExtraW;
- if (thumbnailClipHint.left < 0) {
- thumbnailClipHint.right += thumbnailClipHint.left;
- thumbnailClipHint.left = 0;
- } else if (thumbnailClipHint.right < 0) {
- thumbnailClipHint.left += thumbnailClipHint.right;
- thumbnailClipHint.right = 0;
+ // Update the clip hints. Align to 0,0, crop the remaining.
+ if (isRtl) {
+ thumbnailClipHint.left += availableWidth - croppedWidth;
+ if (thumbnailClipHint.right < 0) {
+ thumbnailClipHint.left += thumbnailClipHint.right;
+ thumbnailClipHint.right = 0;
+ }
+ } else {
+ thumbnailClipHint.right += availableWidth - croppedWidth;
+ if (thumbnailClipHint.left < 0) {
+ thumbnailClipHint.right += thumbnailClipHint.left;
+ thumbnailClipHint.left = 0;
+ }
}
-
- float halfExtraH = (availableHeight - croppedHeight) / 2;
- thumbnailClipHint.top += halfExtraH;
- thumbnailClipHint.bottom += halfExtraH;
+ thumbnailClipHint.bottom += availableHeight - croppedHeight;
if (thumbnailClipHint.top < 0) {
thumbnailClipHint.bottom += thumbnailClipHint.top;
thumbnailClipHint.top = 0;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index cd8ea76..21b1164 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -27,8 +27,8 @@
import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
-import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -42,6 +42,8 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -67,6 +69,7 @@
import android.widget.FrameLayout;
import android.widget.Toast;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
@@ -106,6 +109,7 @@
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
+import java.lang.annotation.Retention;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -117,6 +121,19 @@
private static final String TAG = TaskView.class.getSimpleName();
+ public static final int FLAG_UPDATE_ICON = 1;
+ public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
+
+ public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL;
+
+ /**
+ * Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more
+ * granularity on which components of this task require an update
+ */
+ @Retention(SOURCE)
+ @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
+ public @interface TaskDataChanges {}
+
/**
* The alpha of a black scrim on a page in the carousel as it leaves the screen.
* In the resting position of the carousel, the adjacent pages have about half this scrim.
@@ -262,7 +279,6 @@
private float mFullscreenProgress;
private float mGridProgress;
private float mFullscreenScale = 1;
- private float mGridScale = 1;
private final FullscreenDrawParams mCurrentFullscreenParams;
private final StatefulActivity mActivity;
@@ -275,14 +291,10 @@
private float mTaskResistanceTranslationY;
// The following translation variables should only be used in the same orientation as Launcher.
private float mFullscreenTranslationX;
- private float mAccumulatedFullscreenTranslationX;
private float mBoxTranslationY;
// The following grid translations scales with mGridProgress.
private float mGridTranslationX;
private float mGridTranslationY;
- // Offset translation does not affect scroll calculation.
- private float mGridOffsetTranslationX;
- private float mNonRtlVisibleOffset;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -299,7 +311,6 @@
private boolean mEndQuickswitchCuj;
private View mContextualChipWrapper;
- private View mContextualChip;
private final float[] mIconCenterCoords = new float[2];
private final float[] mChipCenterCoords = new float[2];
@@ -361,7 +372,8 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams);
+ mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams,
+ mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
setOutlineProvider(mOutlineProvider);
}
@@ -448,9 +460,9 @@
}
mModalness = modalness;
mIconView.setAlpha(comp(modalness));
- if (mContextualChip != null) {
- mContextualChip.setScaleX(comp(modalness));
- mContextualChip.setScaleY(comp(modalness));
+ if (mContextualChipWrapper != null) {
+ mContextualChipWrapper.setScaleX(comp(modalness));
+ mContextualChipWrapper.setScaleY(comp(modalness));
}
mDigitalWellBeingToast.updateBannerOffset(modalness,
mCurrentFullscreenParams.mCurrentDrawnInsets.top
@@ -559,7 +571,19 @@
}
}
+ /**
+ * See {@link TaskDataChanges}
+ * @param visible If this task view will be visible to the user in overview or hidden
+ */
public void onTaskListVisibilityChanged(boolean visible) {
+ onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
+ }
+
+ /**
+ * See {@link TaskDataChanges}
+ * @param visible If this task view will be visible to the user in overview or hidden
+ */
+ public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
if (mTask == null) {
return;
}
@@ -570,22 +594,37 @@
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
TaskIconCache iconCache = model.getIconCache();
- mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
- mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
- mIconLoadRequest = iconCache.updateIconInBackground(mTask,
- (task) -> {
- setIcon(task.icon);
- mDigitalWellBeingToast.initialize(mTask);
- });
+
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
+ mTask, thumbnail -> {
+ mSnapshotView.setThumbnail(mTask, thumbnail);
+ });
+ }
+ if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+ mIconLoadRequest = iconCache.updateIconInBackground(mTask,
+ (task) -> {
+ setIcon(task.icon);
+ mDigitalWellBeingToast.initialize(mTask);
+ });
+ }
} else {
- mSnapshotView.setThumbnail(null, null);
- setIcon(null);
- // Reset the task thumbnail reference as well (it will be fetched from the cache or
- // reloaded next time we need it)
- mTask.thumbnail = null;
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ mSnapshotView.setThumbnail(null, null);
+ // Reset the task thumbnail reference as well (it will be fetched from the cache or
+ // reloaded next time we need it)
+ mTask.thumbnail = null;
+ }
+ if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+ setIcon(null);
+ }
}
}
+ private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
+ return (dataChange & flag) == flag;
+ }
+
private void cancelPendingLoadTasks() {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
@@ -631,17 +670,14 @@
}
}
- public int getThumbnailTopMargin() {
- return (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
- }
-
public void setOrientationState(RecentsOrientedState orientationState) {
PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
- int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
- int taskIconMargin = (int) getResources().getDimension(R.dimen.task_icon_top_margin);
- int taskIconHeight = (int) getResources().getDimension(R.dimen.task_thumbnail_icon_size);
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ int taskIconMargin = deviceProfile.overviewTaskMarginPx;
+ int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
switch (orientationHandler.getRotation()) {
case ROTATION_90:
@@ -652,7 +688,7 @@
break;
case ROTATION_180:
iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
- iconParams.bottomMargin = -thumbnailPadding;
+ iconParams.bottomMargin = -snapshotParams.topMargin;
iconParams.leftMargin = iconParams.rightMargin = 0;
iconParams.topMargin = taskIconMargin;
break;
@@ -669,8 +705,12 @@
iconParams.topMargin = taskIconMargin;
break;
}
+ mSnapshotView.setLayoutParams(snapshotParams);
+ iconParams.width = iconParams.height = taskIconHeight;
mIconView.setLayoutParams(iconParams);
mIconView.setRotation(orientationHandler.getDegreesRotated());
+ snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ mSnapshotView.setLayoutParams(snapshotParams);
if (mMenuView != null) {
mMenuView.onRotationChanged();
@@ -690,10 +730,10 @@
.getInterpolation(progress);
mIconView.setScaleX(scale);
mIconView.setScaleY(scale);
- if (mContextualChip != null && mContextualChipWrapper != null) {
+ if (mContextualChipWrapper != null && mContextualChipWrapper != null) {
mContextualChipWrapper.setAlpha(scale);
- mContextualChip.setScaleX(scale);
- mContextualChip.setScaleY(scale);
+ mContextualChipWrapper.setScaleX(Math.min(scale, comp(mModalness)));
+ mContextualChipWrapper.setScaleY(Math.min(scale, comp(mModalness)));
}
mDigitalWellBeingToast.updateBannerOffset(1f - scale,
mCurrentFullscreenParams.mCurrentDrawnInsets.top
@@ -750,9 +790,7 @@
@Override
public void onRecycle() {
- mFullscreenTranslationX = mAccumulatedFullscreenTranslationX = mGridTranslationX =
- mGridTranslationY =
- mGridOffsetTranslationX = mBoxTranslationY = mNonRtlVisibleOffset = 0f;
+ mFullscreenTranslationX = mGridTranslationX = mGridTranslationY = mBoxTranslationY = 0f;
resetViewTransforms();
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
@@ -762,7 +800,7 @@
}
@Override
- public void onPageScroll(ScrollState scrollState) {
+ public void onPageScroll(ScrollState scrollState, boolean gridEnabled) {
// Don't do anything if it's modal.
if (mModalness > 0) {
return;
@@ -799,14 +837,12 @@
int expectedChipHeight = getExpectedViewHeight(view);
float chipOffset = getResources().getDimension(R.dimen.chip_hint_vertical_offset);
layoutParams.bottomMargin = -expectedChipHeight - (int) chipOffset;
- mContextualChip = ((FrameLayout) mContextualChipWrapper).getChildAt(0);
- mContextualChip.setScaleX(0f);
- mContextualChip.setScaleY(0f);
+ mContextualChipWrapper.setScaleX(0f);
+ mContextualChipWrapper.setScaleY(0f);
addView(view, getChildCount(), layoutParams);
- if (mContextualChip != null) {
- mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(50);
- }
if (mContextualChipWrapper != null) {
+ float scale = comp(mModalness);
+ mContextualChipWrapper.animate().scaleX(scale).scaleY(scale).setDuration(50);
mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper);
}
}
@@ -827,7 +863,6 @@
}
View oldContextualChipWrapper = mContextualChipWrapper;
mContextualChipWrapper = null;
- mContextualChip = null;
mChipTouchDelegate = null;
return oldContextualChipWrapper;
}
@@ -836,7 +871,7 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
- setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? (right - left) : 0);
+ setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : right - left);
setPivotY(mSnapshotView.getTop());
} else {
setPivotX((right - left) * 0.5f);
@@ -864,9 +899,8 @@
applyScale();
}
- public void setGridScale(float gridScale) {
- mGridScale = gridScale;
- applyScale();
+ public float getFullscreenScale() {
+ return mFullscreenScale;
}
/**
@@ -885,8 +919,6 @@
float scale = 1;
float fullScreenProgress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress);
scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale);
- float gridProgress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
- scale *= Utilities.mapRange(gridProgress, 1f, mGridScale);
setScaleX(scale);
setScaleY(scale);
}
@@ -921,20 +953,11 @@
applyTranslationY();
}
- private void setFullscreenTranslationX(float fullscreenTranslationX) {
+ public void setFullscreenTranslationX(float fullscreenTranslationX) {
mFullscreenTranslationX = fullscreenTranslationX;
applyTranslationX();
}
- public float getFullscreenTranslationX() {
- return mFullscreenTranslationX;
- }
-
- public void setAccumulatedFullscreenTranslationX(float accumulatedFullscreenTranslationX) {
- mAccumulatedFullscreenTranslationX = accumulatedFullscreenTranslationX;
- applyTranslationX();
- }
-
public void setGridTranslationX(float gridTranslationX) {
mGridTranslationX = gridTranslationX;
applyTranslationX();
@@ -953,19 +976,10 @@
return mGridTranslationY;
}
- public void setGridOffsetTranslationX(float gridOffsetTranslationX) {
- mGridOffsetTranslationX = gridOffsetTranslationX;
- applyTranslationX();
- }
-
- public void setNonRtlVisibleOffset(float nonRtlVisibleOffset) {
- mNonRtlVisibleOffset = nonRtlVisibleOffset;
- }
-
public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
float scrollAdjustment = 0;
if (fullscreenEnabled) {
- scrollAdjustment += mFullscreenTranslationX + mAccumulatedFullscreenTranslationX;
+ scrollAdjustment += mFullscreenTranslationX;
}
if (gridEnabled) {
scrollAdjustment += mGridTranslationX;
@@ -973,22 +987,15 @@
return scrollAdjustment;
}
- public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
- float offsetAdjustment = getScrollAdjustment(fullscreenEnabled, gridEnabled);
- if (gridEnabled) {
- offsetAdjustment += mGridOffsetTranslationX + mNonRtlVisibleOffset;
- }
- return offsetAdjustment;
+ public float getOffsetAdjustment(boolean fullscreenEnabled,boolean gridEnabled) {
+ return getScrollAdjustment(fullscreenEnabled, gridEnabled);
}
- public float getSizeAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+ public float getSizeAdjustment(boolean fullscreenEnabled) {
float sizeAdjustment = 1;
if (fullscreenEnabled) {
sizeAdjustment *= mFullscreenScale;
}
- if (gridEnabled) {
- sizeAdjustment *= mGridScale;
- }
return sizeAdjustment;
}
@@ -999,8 +1006,8 @@
private void applyTranslationX() {
setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
- + getFullscreenTrans(mFullscreenTranslationX + mAccumulatedFullscreenTranslationX)
- + getGridTrans(mGridTranslationX + mGridOffsetTranslationX));
+ + getFullscreenTrans(mFullscreenTranslationX)
+ + getGridTrans(mGridTranslationX));
}
private void applyTranslationY() {
@@ -1050,17 +1057,17 @@
private static final class TaskOutlineProvider extends ViewOutlineProvider {
- private final int mMarginTop;
+ private int mMarginTop;
private FullscreenDrawParams mFullscreenParams;
- TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams) {
- mMarginTop = context.getResources().getDimensionPixelSize(
- R.dimen.task_thumbnail_top_margin);
+ TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams, int topMargin) {
+ mMarginTop = topMargin;
mFullscreenParams = fullscreenParams;
}
- public void setFullscreenParams(FullscreenDrawParams params) {
+ public void updateParams(FullscreenDrawParams params, int topMargin) {
mFullscreenParams = params;
+ mMarginTop = topMargin;
}
@Override
@@ -1183,7 +1190,9 @@
}
thumbnail.setFullscreenParams(mCurrentFullscreenParams);
- mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams);
+ mOutlineProvider.updateParams(
+ mCurrentFullscreenParams,
+ mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
invalidateOutline();
}
@@ -1205,8 +1214,8 @@
void updateTaskSize() {
ViewGroup.LayoutParams params = getLayoutParams();
if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
- final int thumbnailPadding = (int) getResources().getDimension(
- R.dimen.task_thumbnail_top_margin);
+ final int thumbnailPadding =
+ mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
int taskWidth = lastComputedTaskSize.width();
@@ -1243,10 +1252,6 @@
}
setFullscreenScale(fullscreenScale);
- float widthDiff = params.width * (1 - mFullscreenScale);
- setFullscreenTranslationX(
- getLayoutDirection() == LAYOUT_DIRECTION_RTL ? -widthDiff : widthDiff);
-
if (params.width != expectedWidth || params.height != expectedHeight) {
params.width = expectedWidth;
params.height = expectedHeight;
@@ -1254,7 +1259,6 @@
}
} else {
setBoxTranslationY(0);
- setFullscreenTranslationX(0);
setFullscreenScale(1);
if (params.width != ViewGroup.LayoutParams.MATCH_PARENT) {
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index a412b39..4f27e21 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -16,7 +16,15 @@
package com.android.quickstep;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.quickstep.views.RecentsView;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
@@ -31,4 +39,49 @@
outerRule(new NavigationModeSwitchRule(mLauncher)).
around(super.getRulesInsideActivityMonitor());
}
+
+ @Override
+ protected void onLauncherActivityClose(Launcher launcher) {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.finishRecentsAnimation(true, null);
+ }
+ }
+
+ @Override
+ protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
+ boolean isResumed, boolean isStarted) {
+ if (!isInLiveTileMode(launcher, expectedContainerType)) {
+ super.checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
+ } else {
+ assertTrue("[Live Tile] hasBeenResumed() == isStarted(), hasBeenResumed(): "
+ + isResumed, isResumed != isStarted);
+ }
+ }
+
+ @Override
+ protected void checkLauncherStateInOverview(Launcher launcher,
+ ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
+ if (!isInLiveTileMode(launcher, expectedContainerType)) {
+ super.checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
+ isResumed);
+ } else {
+ assertTrue(
+ "[Live Tile] Launcher is not started or has been resumed in state: "
+ + expectedContainerType,
+ isStarted && !isResumed);
+ }
+ }
+
+ private boolean isInLiveTileMode(Launcher launcher,
+ LauncherInstrumentation.ContainerType expectedContainerType) {
+ if (!LIVE_TILE.get()
+ || expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) {
+ return false;
+ }
+
+ RecentsView recentsView = launcher.getOverviewPanel();
+ return recentsView.getSizeStrategy().isInLiveTileMode()
+ && recentsView.getRunningTaskId() != -1;
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 9732cdc..6e19436 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -100,6 +100,7 @@
// The test action.
mLauncher.getBackground().switchToOverview();
}
+ closeLauncherActivity();
mLauncher.pressHome();
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 0fe5432..5ffe315 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -120,6 +120,7 @@
getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
// Test opening a task.
+ startTestActivity(2);
OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
assertNotNull("overview.getCurrentTask() returned null (1)", task);
assertNotNull("OverviewTask.open returned null", task.open());
diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml
new file mode 100644
index 0000000..04bde8f
--- /dev/null
+++ b/res/drawable/add_item_dialog_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+ <solid android:color="?android:attr/colorBackground" />
+ <corners
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/add_item_dialog_button_background.xml b/res/drawable/add_item_dialog_button_background.xml
new file mode 100644
index 0000000..1b4591f
--- /dev/null
+++ b/res/drawable/add_item_dialog_button_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<inset
+ android:insetLeft="@dimen/pin_widget_button_inset_horizontal"
+ android:insetRight="@dimen/pin_widget_button_inset_horizontal"
+ android:insetTop="@dimen/pin_widget_button_inset_vertical"
+ android:insetBottom="@dimen/pin_widget_button_inset_vertical"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:tint="?android:attr/colorAccent" android:shape="rectangle">
+ <corners android:radius="18dp" />
+ <solid android:color="#FFFFFF" />
+ <padding
+ android:left="@dimen/pin_widget_button_padding_horizontal"
+ android:top="@dimen/pin_widget_button_padding_vertical"
+ android:right="@dimen/pin_widget_button_padding_horizontal"
+ android:bottom="@dimen/pin_widget_button_padding_vertical" />
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/bg_widgets_searchbox.xml b/res/drawable/bg_widgets_searchbox.xml
index 81dd2aa..2a50a51 100644
--- a/res/drawable/bg_widgets_searchbox.xml
+++ b/res/drawable/bg_widgets_searchbox.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FFFFF7" />
+ <solid android:color="?android:attr/textColorPrimaryInverse" />
<corners android:radius="24dp" />
</shape>
\ No newline at end of file
diff --git a/res/drawable/gm_edit_24.xml b/res/drawable/gm_edit_24.xml
new file mode 100644
index 0000000..59a0dc2
--- /dev/null
+++ b/res/drawable/gm_edit_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20.41,4.94l-1.35,-1.35c-0.78,-0.78 -2.05,-0.78 -2.83,0L3,16.82L3,21h4.18L20.41,7.77c0.79,-0.78 0.79,-2.05 0,-2.83zM6.41,19.06L5,19v-1.36l9.82,-9.82 1.41,1.41 -9.82,9.83z"/>
+</vector>
diff --git a/res/drawable/ic_expand_less.xml b/res/drawable/ic_expand_less.xml
index 8360cee..cc16083 100644
--- a/res/drawable/ic_expand_less.xml
+++ b/res/drawable/ic_expand_less.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/textColorHint">
+ android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="#FF000000"
android:pathData="M18.59,16.41L20,15l-8,-8 -8,8 1.41,1.41L12,9.83"/>
diff --git a/res/drawable/ic_expand_more.xml b/res/drawable/ic_expand_more.xml
index 49e24f6..ecbce7f 100644
--- a/res/drawable/ic_expand_more.xml
+++ b/res/drawable/ic_expand_more.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/textColorHint">
+ android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="#FF000000"
android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
diff --git a/res/drawable/middle_item_primary.xml b/res/drawable/middle_item_primary.xml
new file mode 100644
index 0000000..0c04ea1
--- /dev/null
+++ b/res/drawable/middle_item_primary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/popupColorPrimary"/>
+ <corners android:radius="@dimen/popup_smaller_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/single_item_primary.xml b/res/drawable/single_item_primary.xml
new file mode 100644
index 0000000..1c0889b
--- /dev/null
+++ b/res/drawable/single_item_primary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/popupColorPrimary"/>
+ <corners android:radius="@dimen/popup_single_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/single_item_secondary.xml b/res/drawable/single_item_secondary.xml
new file mode 100644
index 0000000..4edc481
--- /dev/null
+++ b/res/drawable/single_item_secondary.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/popupColorSecondary"/>
+ <corners android:radius="@dimen/popup_single_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/widget_reconfigure_button_frame.xml b/res/drawable/widget_reconfigure_button_frame.xml
new file mode 100644
index 0000000..37d93ad
--- /dev/null
+++ b/res/drawable/widget_reconfigure_button_frame.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:width="@dimen/widget_reconfigure_button_size"
+ android:height="@dimen/widget_reconfigure_button_size">
+ <shape
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="@dimen/widget_reconfigure_button_corner_radius" />
+ </shape>
+ </item>
+ <item
+ android:gravity="center"
+ android:padding="@dimen/widget_reconfigure_button_padding"
+ android:drawable="@drawable/gm_edit_24"
+ android:color="?android:attr/colorPrimary" />
+</layer-list>
diff --git a/res/drawable/widgets_list_bottom_ripple.xml b/res/drawable/widgets_list_bottom_ripple.xml
new file mode 100644
index 0000000..3a26091
--- /dev/null
+++ b/res/drawable/widgets_list_bottom_ripple.xml
@@ -0,0 +1,41 @@
+<?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="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:topRightRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
+ <corners
+ android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:topRightRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_middle_ripple.xml b/res/drawable/widgets_list_middle_ripple.xml
new file mode 100644
index 0000000..da025d7
--- /dev/null
+++ b/res/drawable/widgets_list_middle_ripple.xml
@@ -0,0 +1,42 @@
+<?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="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:topRightRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+ </shape>
+ </item>
+
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
+ <corners
+ android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:topRightRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_single_item_ripple.xml b/res/drawable/widgets_list_single_item_ripple.xml
new file mode 100644
index 0000000..b8b6f42
--- /dev/null
+++ b/res/drawable/widgets_list_single_item_ripple.xml
@@ -0,0 +1,41 @@
+<?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="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
+ <corners
+ android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_top_ripple.xml b/res/drawable/widgets_list_top_ripple.xml
new file mode 100644
index 0000000..6efc3e1
--- /dev/null
+++ b/res/drawable/widgets_list_top_ripple.xml
@@ -0,0 +1,42 @@
+<?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="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+ </shape>
+ </item>
+
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
+ <corners
+ android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+ android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index b1a1efe..d5e7333 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -17,70 +17,51 @@
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_item_confirmation"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:padding="24dp"
android:orientation="vertical">
- <ScrollView
+
+ <TextView
+ style="@style/TextHeadline"
+ android:id="@+id/widget_appName"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="24sp"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:maxLines="1" />
+
+ <include layout="@layout/widget_cell"
+ android:id="@+id/widget_cell"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:clipToPadding="false">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="20dp"
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:paddingTop="4dp"
- android:text="@string/add_item_request_drag_hint" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/colorPrimaryDark"
- android:theme="?attr/widgetsTheme">
-
- <com.android.launcher3.widget.WidgetCell
- android:id="@+id/widget_cell"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_weight="1"
- android:background="?android:attr/colorPrimaryDark"
- android:focusable="true"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
-
- <include layout="@layout/widget_cell_content" />
-
- </com.android.launcher3.widget.WidgetCell>
- </FrameLayout>
- </LinearLayout>
- </ScrollView>
+ android:layout_marginVertical="16dp" />
<LinearLayout
- style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
- android:paddingBottom="4dp"
- android:paddingEnd="12dp"
- android:paddingStart="12dp"
- android:paddingTop="4dp" >
+ android:padding="8dp"
+ android:orientation="horizontal">
<Button
- style="?android:attr/buttonBarButtonStyle"
+ style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onCancelClick"
android:text="@android:string/cancel" />
+
+ <Space
+ android:layout_width="4dp"
+ android:layout_height="wrap_content" />
+
<Button
- style="?android:attr/buttonBarButtonStyle"
+ style="@style/Widget.DeviceDefault.Button.Rounded.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onPlaceAutomaticallyClick"
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 8ed16c7..24d764e 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -44,7 +44,6 @@
</com.android.launcher3.allapps.FloatingHeaderView>
<include
- android:id="@id/search_container_all_apps"
layout="@layout/search_container_all_apps"/>
<include layout="@layout/all_apps_fast_scroller" />
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index c684881..2accd2d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -26,6 +26,7 @@
android:clipChildren="true"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
+ android:paddingTop="@dimen/all_apps_header_top_padding"
launcher:pageIndicator="@+id/tabs" >
<include layout="@layout/all_apps_rv_layout" />
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index dfce946..53db5ed 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -26,6 +26,7 @@
<!-- Frame -->
<ImageView
+ android:id="@+id/widget_resize_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
@@ -34,6 +35,7 @@
<!-- Left -->
<ImageView
+ android:id="@+id/widget_resize_left_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
@@ -43,6 +45,7 @@
<!-- Top -->
<ImageView
+ android:id="@+id/widget_resize_top_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
@@ -52,6 +55,7 @@
<!-- Right -->
<ImageView
+ android:id="@+id/widget_resize_right_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
@@ -61,6 +65,7 @@
<!-- Bottom -->
<ImageView
+ android:id="@+id/widget_resize_bottom_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
@@ -68,5 +73,17 @@
android:src="@drawable/ic_widget_resize_handle"
android:tint="?android:attr/colorAccent" />
+ <ImageButton
+ android:id="@+id/widget_reconfigure_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/widget_reconfigure_button_padding"
+ android:layout_gravity="bottom|end"
+ android:layout_marginBottom="@dimen/widget_reconfigure_button_margin"
+ android:layout_marginEnd="@dimen/widget_reconfigure_button_margin"
+ android:src="@drawable/widget_reconfigure_button_frame"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone" />
+
</FrameLayout>
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 840a8b7..0d11b50 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -19,6 +19,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height"
+ android:background="@drawable/middle_item_primary"
android:theme="@style/PopupItem" >
<com.android.launcher3.shortcuts.DeepShortcutTextView
@@ -31,6 +32,8 @@
android:paddingEnd="@dimen/popup_padding_end"
android:drawableEnd="@drawable/ic_drag_handle"
android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
+ android:singleLine="true"
+ android:ellipsize="end"
android:textSize="14sp"
android:textColor="?android:attr/textColorPrimary"
launcher:layoutHorizontal="true"
@@ -45,12 +48,4 @@
android:layout_gravity="start|center_vertical"
android:background="@drawable/ic_deepshortcut_placeholder"/>
- <View
- android:id="@+id/divider"
- android:layout_width="@dimen/deep_shortcuts_divider_width"
- android:layout_height="@dimen/popup_item_divider_height"
- android:layout_gravity="end|bottom"
- android:visibility="gone"
- android:background="?attr/popupColorTertiary" />
-
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml
index 20bb5b8..3898365 100644
--- a/res/layout/longpress_options_menu.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -18,7 +18,6 @@
android:id="@+id/deep_shortcuts_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="?attr/popupColorPrimary"
android:clipToPadding="false"
android:clipChildren="false"
android:elevation="@dimen/deep_shortcuts_elevation"
diff --git a/res/layout/notification_content.xml b/res/layout/notification_content.xml
index d01be01..147aa30 100644
--- a/res/layout/notification_content.xml
+++ b/res/layout/notification_content.xml
@@ -96,14 +96,6 @@
</com.android.launcher3.notification.NotificationMainView>
- <!-- Divider -->
- <View
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="@dimen/popup_item_divider_height"
- android:layout_below="@id/main_view"
- android:background="?attr/popupColorTertiary" />
-
<!-- Footer -->
<com.android.launcher3.notification.NotificationFooterLayout
android:id="@+id/footer"
diff --git a/res/layout/notification_gutter.xml b/res/layout/notification_gutter.xml
index 10e7f7d..9a3e55a 100644
--- a/res/layout/notification_gutter.xml
+++ b/res/layout/notification_gutter.xml
@@ -16,6 +16,5 @@
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="4dp"
- android:layout_marginTop="4dp"
- android:background="@drawable/bg_notification_content" />
\ No newline at end of file
+ android:layout_height="0dp"
+ android:layout_marginTop="@dimen/popup_margin" />
\ No newline at end of file
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index c737407..04822fd 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -19,8 +19,15 @@
android:id="@+id/deep_shortcuts_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="?attr/popupColorPrimary"
android:clipToPadding="false"
android:clipChildren="false"
android:elevation="@dimen/deep_shortcuts_elevation"
- android:orientation="vertical" />
\ No newline at end of file
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/notification_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:background="?attr/popupColorPrimary"
+ android:orientation="vertical"/>
+</com.android.launcher3.popup.PopupContainerWithArrow>
\ No newline at end of file
diff --git a/res/layout/search_container_hotseat.xml b/res/layout/search_container_hotseat.xml
new file mode 100644
index 0000000..8f12ca0
--- /dev/null
+++ b/res/layout/search_container_hotseat.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index c620e2a..9f45f30 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -19,6 +19,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height"
+ android:background="@drawable/middle_item_primary"
android:theme="@style/PopupItem" >
<com.android.launcher3.BubbleTextView
@@ -30,7 +31,8 @@
android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
android:paddingEnd="@dimen/popup_padding_end"
android:textSize="14sp"
- android:maxLines="1"
+ android:singleLine="true"
+ android:ellipsize="end"
android:textColor="?android:attr/textColorPrimary"
launcher:iconDisplay="shortcut_popup"
launcher:layoutHorizontal="true"
@@ -44,12 +46,4 @@
android:layout_gravity="start|center_vertical"
android:backgroundTint="?android:attr/textColorTertiary"/>
- <View
- android:id="@+id/divider"
- android:layout_width="@dimen/deep_shortcuts_divider_width"
- android:layout_height="@dimen/popup_item_divider_height"
- android:layout_gravity="end|bottom"
- android:visibility="gone"
- android:background="?attr/popupColorTertiary" />
-
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index a340f4f..f992248 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -21,7 +21,7 @@
android:layout_height="@dimen/system_shortcut_header_height"
android:orientation="horizontal"
android:gravity="end|center_vertical"
- android:background="?attr/popupColorSecondary"
+ android:background="@drawable/single_item_secondary"
android:clipToPadding="true">
<Space android:layout_width="0dp"
diff --git a/res/layout/taskbar_view.xml b/res/layout/taskbar_view.xml
new file mode 100644
index 0000000..96ae43d
--- /dev/null
+++ b/res/layout/taskbar_view.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<Space
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone" />
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index d38a77a..8b18857 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -19,7 +19,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/round_rect_folder"
- android:elevation="5dp"
android:orientation="vertical" >
<com.android.launcher3.folder.FolderPagedView
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 65a49ab..30bd8b1 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -18,12 +18,14 @@
android:layout_height="wrap_content">
<!-- The image of the widget. This view does not support padding. Any placement adjustment
- should be done using margins. -->
+ should be done using margins. Width & height are set at runtime after scaling the preview
+ image. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
+ android:importantForAccessibility="no"
android:layout_marginVertical="8dp" />
<!-- The name of the widget. -->
@@ -36,28 +38,29 @@
android:gravity="center_horizontal"
android:singleLine="true"
android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/widget_cell_font_size" />
- <!-- The original dimensions of the widget (can't be the same text as above due to different
- style. -->
+ <!-- The original dimensions of the widget -->
<TextView
android:id="@+id/widget_dims"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:alpha="0.8" />
+ android:textSize="@dimen/widget_cell_font_size"
+ android:alpha="0.7" />
<TextView
android:id="@+id/widget_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:textSize="12sp"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:textColor="?android:attr/textColorSecondary"
android:maxLines="2"
android:ellipsize="end"
- android:fadingEdge="horizontal" />
+ android:fadingEdge="horizontal"
+ android:alpha="0.7" />
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index c1b2cbf..8002d1d 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -19,41 +19,12 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="28dp"
+ android:paddingTop="16dp"
android:background="@drawable/top_round_rect_primary"
android:elevation="@dimen/deep_shortcuts_elevation"
android:layout_gravity="bottom"
android:theme="?attr/widgetsTheme">
- <TextView
- style="@style/TextHeadline"
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="24sp"/>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:paddingTop="4dp"
- android:fontFamily="sans-serif"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="14sp"
- android:text="@string/long_press_widget_to_add"/>
-
- <ScrollView
- android:id="@+id/widgets_table_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="45dp"
- android:layout_marginBottom="40dp">
- <include layout="@layout/widgets_table_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal" />
- </ScrollView>
+ <include layout="@layout/widgets_bottom_sheet_content" />
</com.android.launcher3.widget.WidgetsBottomSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
new file mode 100644
index 0000000..a9d523a
--- /dev/null
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <View
+ android:layout_width="48dp"
+ android:layout_height="2dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/textColorSecondary"/>
+ <TextView
+ style="@style/TextHeadline"
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="24sp"/>
+
+ <ScrollView
+ android:id="@+id/widgets_table_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fadeScrollbars="false"
+ android:layout_marginVertical="16dp">
+ <include layout="@layout/widgets_table_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal" />
+ </ScrollView>
+</merge>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 226c4f7..172284b 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -25,7 +25,7 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?android:attr/colorPrimary"
+ android:background="?android:attr/colorBackgroundFloating"
android:elevation="4dp">
<TextView
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 8125db8..ae877d4 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -16,11 +16,6 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <include layout="@layout/personal_work_tabs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginHorizontal="16dp" />
-
<com.android.launcher3.workprofile.PersonalWorkPagedView
android:id="@+id/widgets_view_pager"
android:layout_width="match_parent"
@@ -43,4 +38,8 @@
</com.android.launcher3.workprofile.PersonalWorkPagedView>
+ <include layout="@layout/personal_work_tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp" />
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
index 6182255..e5df175 100644
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -18,14 +18,17 @@
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="16dp"
+ android:paddingHorizontal="16dp"
+ android:layout_marginBottom="16dp"
android:orientation="vertical">
<View
android:id="@+id/collapse_handle"
android:layout_width="48dp"
android:layout_height="2dp"
+ android:layout_marginTop="16dp"
+ android:elevation="2dp"
android:layout_gravity="center_horizontal"
- android:background="@color/popup_color_primary_dark"/>
+ android:background="?android:attr/textColorSecondary"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
@@ -33,6 +36,14 @@
android:gravity="center_horizontal"
android:textSize="24sp"
android:layout_marginTop="16dp"
+ android:textColor="?android:attr/textColorSecondary"
android:text="@string/widget_button_text"/>
<include layout="@layout/widgets_search_bar"/>
+
+ <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+ android:id="@+id/recommended_widget_table"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:visibility="gone" />
</LinearLayout>
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 1590286..598041c 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -18,7 +18,9 @@
android:id="@+id/widgets_list_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
+ android:layout_marginHorizontal="8dp"
+ android:background="@drawable/widgets_list_middle_ripple"
+ android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"
android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
android:orientation="horizontal">
@@ -54,6 +56,8 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:alpha="0.7"
tools:text="m widgets, n shortcuts" />
</LinearLayout>
@@ -68,6 +72,7 @@
android:layout_gravity="center_vertical"
android:layout_alignParentEnd="true"
android:clickable="false"
+ android:importantForAccessibility="no"
android:button="@drawable/widgets_tray_expand_button"/>
</com.android.launcher3.widget.picker.WidgetsListHeader>
\ No newline at end of file
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index 252637d..e3836df 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<com.android.launcher3.widget.picker.search.WidgetsSearchBar
+<com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widgets_search_bar"
android:layout_width="match_parent"
@@ -7,27 +7,31 @@
android:orientation="horizontal"
android:layout_marginTop="16dp"
android:background="@drawable/bg_widgets_searchbox"
- android:padding="12dp"
- android:visibility="gone">
+ android:elevation="2dp">
- <EditText
+ <com.android.launcher3.ExtendedEditText
android:id="@+id/widgets_search_bar_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:padding="12dp"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_allapps_search"
android:background="@null"
android:hint="@string/widgets_full_sheet_search_bar_hint"
android:maxLines="1"
android:layout_weight="1"
- android:inputType="text"/>
+ android:inputType="text"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textColorHint="?android:attr/textColorTertiary"/>
<ImageButton
android:id="@+id/widgets_search_cancel_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
+ android:padding="8dp"
android:src="@drawable/ic_gm_close_24"
android:background="?android:selectableItemBackground"
android:layout_gravity="center"
+ android:contentDescription="@string/widgets_full_sheet_cancel_button_description"
android:visibility="gone"/>
-</com.android.launcher3.widget.picker.search.WidgetsSearchBar>
\ No newline at end of file
+</com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar>
\ No newline at end of file
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index c4dfe7e..0b5f0b9 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -19,4 +19,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
- android:background="?android:attr/colorPrimaryDark" />
+ android:background="@drawable/widgets_list_middle_ripple"
+ android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"/>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index 510e1f4..07a5096 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -21,6 +21,8 @@
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+ <item name="android:windowBackground">@drawable/add_item_dialog_background</item>
+ <item name="android:windowNoTitle">true</item>
</style>
</resources>
\ No newline at end of file
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 09bdaaf..d50115b 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,3 +1,6 @@
<resources>
<bool name="allow_rotation">true</bool>
+
+ <!-- Hotseat -->
+ <bool name="hotseat_transpose_layout_with_orientation">false</bool>
</resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index ec07591..33fc553 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -3,7 +3,4 @@
<!-- All Apps & Widgets -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
-
-<!-- Hotseat -->
- <bool name="hotseat_transpose_layout_with_orientation">false</bool>
</resources>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 6baf39e..24aac10 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -17,17 +17,17 @@
*/
-->
<resources>
- <color name="popup_color_primary_light">@android:color/system_primary_50</color>
- <color name="popup_color_secondary_light">@android:color/system_primary_100</color>
- <color name="popup_color_tertiary_light">@android:color/system_primary_300</color>
- <color name="popup_color_primary_dark">@android:color/system_primary_800</color>
- <color name="popup_color_secondary_dark">@android:color/system_primary_900</color>
- <color name="popup_color_tertiary_dark">@android:color/system_primary_700</color>
+ <color name="popup_color_primary_light">@android:color/system_neutral1_50</color>
+ <color name="popup_color_secondary_light">@android:color/system_neutral2_100</color>
+ <color name="popup_color_tertiary_light">@android:color/system_neutral2_300</color>
+ <color name="popup_color_primary_dark">@android:color/system_neutral1_800</color>
+ <color name="popup_color_secondary_dark">@android:color/system_neutral1_900</color>
+ <color name="popup_color_tertiary_dark">@android:color/system_neutral2_700</color>
- <color name="workspace_text_color_light">@android:color/system_primary_50</color>
- <color name="workspace_text_color_dark">@android:color/system_primary_900</color>
+ <color name="workspace_text_color_light">@android:color/system_neutral1_50</color>
+ <color name="workspace_text_color_dark">@android:color/system_neutral1_900</color>
- <color name="text_color_primary_dark">@android:color/system_primary_50</color>
- <color name="text_color_secondary_dark">@android:color/system_primary_200</color>
- <color name="text_color_tertiary_dark">@android:color/system_primary_400</color>
+ <color name="text_color_primary_dark">@android:color/system_neutral1_50</color>
+ <color name="text_color_secondary_dark">@android:color/system_neutral2_200</color>
+ <color name="text_color_tertiary_dark">@android:color/system_neutral2_400</color>
</resources>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 4078ef4..99f6c75 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -33,7 +33,6 @@
<attr name="workspaceKeyShadowColor" format="color" />
<attr name="workspaceStatusBarScrim" format="reference" />
<attr name="widgetsTheme" format="reference" />
- <attr name="loadingIconColor" format="color" />
<attr name="iconOnlyShortcutColor" format="color" />
<attr name="eduHalfSheetBGColor" format="color" />
@@ -44,7 +43,6 @@
<attr name="folderTextColor" format="color" />
<attr name="folderHintColor" format="color" />
<attr name="workProfileOverlayTextColor" format="color" />
- <attr name="disabledIconAlpha" format="float" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
@@ -129,11 +127,13 @@
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
<attr name="dbFile" format="string" />
- <!-- numAllAppsColumns defaults to numColumns, if not specified -->
- <attr name="numAllAppsColumns" format="integer" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
<attr name="isScalable" format="boolean" />
+ <attr name="devicePaddingId" format="reference" />
+
+ <!-- whether the grid option is shown to the user -->
+ <attr name="visible" format="boolean" />
</declare-styleable>
@@ -170,6 +170,9 @@
<attr name="allAppsIconSize" format="float" />
<!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
<attr name="allAppsIconTextSize" format="float" />
+
+ <!-- numAllAppsColumns defaults to GridDisplayOption.numColumns, if not specified -->
+ <attr name="numAllAppsColumns" format="integer" />
</declare-styleable>
<declare-styleable name="CellLayout">
diff --git a/res/values/config.xml b/res/values/config.xml
index 65e2ab3..57f626c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -87,8 +87,6 @@
<!-- Default packages -->
<string name="wallpaper_picker_package" translatable="false"></string>
- <string name="calendar_component_name" translatable="false"></string>
- <string name="clock_component_name" translatable="false"></string>
<string name="local_colors_extraction_class" translatable="false"></string>
<!-- Accessibility actions -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d135b43..1fccdf3 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -29,6 +29,8 @@
<dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
<dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
+ <dimen name="two_panel_home_side_padding">18dp</dimen>
+
<!-- Hotseat -->
<dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
<dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
@@ -54,6 +56,12 @@
<dimen name="resize_frame_background_padding">24dp</dimen>
<dimen name="resize_frame_margin">22dp</dimen>
+ <!-- App widget reconfigure button -->
+ <dimen name="widget_reconfigure_button_corner_radius">14dp</dimen>
+ <dimen name="widget_reconfigure_button_padding">6dp</dimen>
+ <dimen name="widget_reconfigure_button_margin">32dp</dimen>
+ <dimen name="widget_reconfigure_button_size">36dp</dimen>
+
<!-- Fast scroll -->
<dimen name="fastscroll_track_min_width">6dp</dimen>
<dimen name="fastscroll_track_max_width">8dp</dimen>
@@ -107,8 +115,14 @@
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
<dimen name="widget_cell_horizontal_padding">16dp</dimen>
+ <dimen name="widget_cell_font_size">14sp</dimen>
+
+
+ <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
+ <dimen name="widget_list_content_corner_radius">4dp</dimen>
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
+ <dimen name="widget_list_entry_bottom_margin">2dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
@@ -130,6 +144,12 @@
<dimen name="shortcut_preview_padding_right">0dp</dimen>
<dimen name="shortcut_preview_padding_top">0dp</dimen>
+<!-- Pin widget dialog -->
+ <dimen name="pin_widget_button_padding_horizontal">8dp</dimen>
+ <dimen name="pin_widget_button_padding_vertical">4dp</dimen>
+ <dimen name="pin_widget_button_inset_horizontal">4dp</dimen>
+ <dimen name="pin_widget_button_inset_vertical">6dp</dimen>
+
<!-- Dragging -->
<!-- Drag padding to add to the bottom of drop targets -->
<dimen name="drop_target_drag_padding">14dp</dimen>
@@ -176,16 +196,17 @@
<dimen name="pending_widget_elevation">2dp</dimen>
<!-- Deep shortcuts -->
- <dimen name="deep_shortcuts_elevation">9dp</dimen>
- <!-- also update deep_shortcuts_divider_width -->
- <dimen name="bg_popup_item_width">234dp</dimen>
+ <dimen name="deep_shortcuts_elevation">0dp</dimen>
+ <dimen name="bg_popup_item_width">216dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
- <dimen name="bg_popup_item_condensed_height">48dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
- <dimen name="deep_shortcut_icon_size">36dp</dimen>
- <dimen name="deep_shortcut_drawable_padding">8dp</dimen>
+ <dimen name="deep_shortcut_icon_size">32dp</dimen>
+ <dimen name="popup_margin">2dp</dimen>
+ <dimen name="popup_single_item_radius">100dp</dimen>
+ <dimen name="popup_smaller_radius">4dp</dimen>
+ <dimen name="deep_shortcut_drawable_padding">12dp</dimen>
<dimen name="deep_shortcut_drag_handle_size">16dp</dimen>
<dimen name="popup_padding_start">10dp</dimen>
<dimen name="popup_padding_end">16dp</dimen>
@@ -194,16 +215,14 @@
<dimen name="popup_arrow_height">10dp</dimen>
<dimen name="popup_arrow_vertical_offset">-1dp</dimen>
<!-- popup_padding_start + deep_shortcut_icon_size / 2 -->
- <dimen name="popup_arrow_horizontal_center_offset">28dp</dimen>
+ <dimen name="popup_arrow_horizontal_center_offset">26dp</dimen>
<dimen name="popup_arrow_corner_radius">2dp</dimen>
<!-- popup_padding_start + icon_size + 10dp -->
- <dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
- <!-- popup_item_width - deep_shortcuts_text_padding_start -->
- <dimen name="deep_shortcuts_divider_width">178dp</dimen>
+ <dimen name="deep_shortcuts_text_padding_start">52dp</dimen>
<dimen name="system_shortcut_icon_size">24dp</dimen>
- <!-- popup_arrow_center_start - system_shortcut_icon_size / 2 -->
+ <!-- popup_arrow_horizontal_center_offset - system_shortcut_icon_size / 2 -->
<dimen name="system_shortcut_margin_start">16dp</dimen>
- <dimen name="system_shortcut_header_height">48dp</dimen>
+ <dimen name="system_shortcut_header_height">56dp</dimen>
<dimen name="system_shortcut_header_icon_touch_size">48dp</dimen>
<!-- (touch_size - icon_size) / 2 -->
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
@@ -270,4 +289,12 @@
<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="taskbar_size">0dp</dimen>
+ <!-- Size of the maximum radius for the enforced rounded rectangles. -->
+ <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
+
+<!-- Overview placeholder to compile in Launcer3 without Quickstep -->
+ <dimen name="task_thumbnail_icon_size">0dp</dimen>
+ <dimen name="task_thumbnail_icon_size_grid">0dp</dimen>
+ <dimen name="overview_task_margin">0dp</dimen>
+
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0600cae..1371e91 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -81,15 +81,19 @@
<!-- Search bar text shown in the popup view showing all available widgets installed on the
device. [CHAR_LIMIT=50] -->
<string name="widgets_full_sheet_search_bar_hint">Search</string>
+ <!-- Spoken text for screen readers. This text lets a user know that the button is used to clear
+ the text that the user entered in the search box. [CHAR_LIMIT=none] -->
+ <string name="widgets_full_sheet_cancel_button_description">Clear text from search box</string>
<!-- Text shown when there is no widgets shown in the popup view showing all available widgets
installed on the device. [CHAR_LIMIT=none] -->
<string name="no_widgets_available">No widgets available</string>
+ <!-- Text shown when there are no matching widget search results for user's search query.
+ [CHAR_LIMIT=none] -->
+ <string name="no_search_results">No search results</string>
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_search_bar_hint">Search apps</string>
- <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
- <string name="all_apps_on_device_search_bar_hint">Search this phone and more…</string>
<!-- Loading apps text. [CHAR_LIMIT=50] -->
<string name="all_apps_loading_message">Loading apps…</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
@@ -268,8 +272,6 @@
<string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
<!-- Strings for widgets & more in the popup container/bottom sheet -->
- <!-- Title for a bottom sheet that shows widgets for a particular app -->
- <string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> widgets</string>
<!-- Accessibility title for the popup containing a list of widgets. [CHAR_LIMIT=50] -->
<string name="widgets_list">Widgets list</string>
@@ -378,9 +380,18 @@
<!--- heading shown when user opens work apps tab while work apps are paused -->
<string name="work_apps_paused_title">Work profile is paused</string>
<!--- body shown when user opens work apps tab while work apps are paused -->
- <string name="work_apps_paused_body">Work apps can\'t send you notifications, use your battery, or access your location</string>
+ <string name="work_apps_paused_body">Work apps can’t send you notifications, use your battery, or access your location</string>
<!-- content description for paused work apps list -->
- <string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
+ <string name="work_apps_paused_content_description">Work profile is paused. Work apps can’t send you notifications, use your battery, or access your location</string>
+ <!-- string shown in educational banner about work profile -->
+ <string name="work_apps_paused_edu_banner">Work apps are badged and visible to your IT admin</string>
+ <!-- button string shown to dismiss work tab education -->
+ <string name="work_apps_paused_edu_accept">Got it</string>
+
+ <!-- button string shown pause work profile -->
+ <string name="work_apps_pause_btn_text">Pause work apps</string>
+ <!-- button string shown enable work profile -->
+ <string name="work_apps_enable_btn_text">Turn on</string>
<!-- A hint shown in launcher settings develop options filter box -->
<string name="developer_options_filter_hint">Filter</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index adc2238..a27cdac 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -145,6 +145,8 @@
<style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
+ <item name="android:windowBackground">@drawable/add_item_dialog_background</item>
+ <item name="android:windowNoTitle">true</item>
</style>
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -281,4 +283,8 @@
<item name="android:colorControlHighlight">#DFE1E5</item>
<item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
</style>
+
+ <style name="Widget.DeviceDefault.Button.Rounded.Colored" parent="@android:style/Widget.DeviceDefault.Button.Colored">
+ <item name="android:background">@drawable/add_item_dialog_button_background</item>
+ </style>
</resources>
diff --git a/res/xml/size_limits.xml b/res/xml/size_limits_80x104.xml
similarity index 98%
rename from res/xml/size_limits.xml
rename to res/xml/size_limits_80x104.xml
index ba57014..e11bc5e 100644
--- a/res/xml/size_limits.xml
+++ b/res/xml/size_limits_80x104.xml
@@ -38,7 +38,7 @@
<workspaceBottomPadding
launcher:a="0.50"
launcher:b="0"
- launcher:c="-16dp"/>
+ launcher:c="16dp"/>
<hotseatBottomPadding
launcher:a="0.50"
launcher:b="0"
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
index c738df9..bf32362 100644
--- a/robolectric_tests/Android.bp
+++ b/robolectric_tests/Android.bp
@@ -16,31 +16,43 @@
// Launcher Robolectric test target.
//
// "robolectric_android-all-stub", not needed, we write our own stubs
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_apps_Launcher3_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["packages_apps_Launcher3_license"],
+}
+
+filegroup {
+ name: "launcher3-robolectric-resources",
+ path: "resources",
+ srcs: ["resources/*"],
+}
+
+filegroup {
+ name: "launcher3-robolectric-src",
+ srcs: ["src/**/*.java"],
+}
+
android_robolectric_test {
name: "LauncherRoboTests",
srcs: [
- "src/**/*.java",
+ ":launcher3-robolectric-src",
+ ":launcher3-test-src-common",
],
- java_resource_dirs: [
- "resources",
- "res",
- "config",
- ],
+ java_resources: [":launcher3-robolectric-resources"],
static_libs: [
"truth-prebuilt",
- "Launcher3TestCommon",
"androidx.test.runner",
"androidx.test.rules",
"mockito-robolectric-prebuilt",
],
- //robolectric_prebuilt_version: "4.4",
- libs: [
- "platform-robolectric-4.4-prebuilt",
- ],
+ robolectric_prebuilt_version: "4.5.1",
instrumentation_for: "Launcher3",
test_options: {
timeout: 36000,
},
}
-
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/resources/robolectric.properties
similarity index 98%
rename from robolectric_tests/config/robolectric.properties
rename to robolectric_tests/resources/robolectric.properties
index 1b170e1..abb6968 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/resources/robolectric.properties
@@ -1,4 +1,4 @@
-sdk=29
+sdk=30
shadows= \
com.android.launcher3.shadows.LShadowAppPredictionManager \
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
index 34cb2ad..4d151f1 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -54,6 +54,7 @@
*/
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
+@org.junit.Ignore
public class LauncherUIScrollTest {
private Context mTargetContext;
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index e1214ff..6b5678c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -82,7 +82,7 @@
mTestProfile.numColumns = 5;
mUserHandle = Process.myUserHandle();
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
- mIconCache, null, null);
+ mIconCache, null, null, null);
mAdapter.registerAdapterDataObserver(mListener);
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index e8c11da..12a092d 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -34,6 +34,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
@@ -78,6 +79,8 @@
@Mock
private DeviceProfile mDeviceProfile;
@Mock
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+ @Mock
private OnHeaderClickListener mOnHeaderClickListener;
@Before
@@ -97,8 +100,15 @@
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
+ WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+ LayoutInflater.from(mTestActivity),
+ mWidgetPreviewLoader,
+ mIconCache,
+ /* iconClickListener= */ view -> {},
+ /* iconLongClickListener= */ view -> false,
+ /* searchBarUIHelper= */ null);
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
- LayoutInflater.from(mTestActivity), mOnHeaderClickListener);
+ LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
}
@After
@@ -115,7 +125,7 @@
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -133,7 +143,7 @@
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index 07fbfd2..e090341 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -34,6 +34,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
@@ -78,6 +79,8 @@
@Mock
private DeviceProfile mDeviceProfile;
@Mock
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+ @Mock
private OnHeaderClickListener mOnHeaderClickListener;
@Before
@@ -97,8 +100,15 @@
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
+ WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+ LayoutInflater.from(mTestActivity),
+ mWidgetPreviewLoader,
+ mIconCache,
+ /* iconClickListener= */ view -> {},
+ /* iconLongClickListener= */ view -> false,
+ /* searchBarUIHelper= */ null);
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
- LayoutInflater.from(mTestActivity), mOnHeaderClickListener);
+ LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
}
@After
@@ -115,7 +125,7 @@
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -134,7 +144,7 @@
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 8a0cf34..0935d1c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -106,12 +106,20 @@
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
+ WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+ LayoutInflater.from(mTestActivity),
+ mWidgetPreviewLoader,
+ mIconCache,
+ /* iconClickListener= */ view -> {},
+ /* iconLongClickListener= */ view -> false,
+ /* searchBarUIHelper= */ null);
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
mContext,
LayoutInflater.from(mTestActivity),
mOnIconClickListener,
mOnLongClickListener,
- mWidgetPreviewLoader);
+ mWidgetPreviewLoader,
+ widgetsListAdapter);
}
@After
@@ -127,7 +135,7 @@
APP_NAME,
TEST_PACKAGE,
/* numOfWidgets= */ 3);
- mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
shadowOf(getMainLooper()).idle();
// THEN the table container has one row, which contains 3 widgets.
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index 7fc9650..4e6f17c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -24,9 +24,9 @@
import android.content.Context;
import android.view.View;
-import android.widget.EditText;
import android.widget.ImageButton;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -45,7 +45,7 @@
private WidgetsSearchBarController mController;
private Context mContext;
- private EditText mEditText;
+ private ExtendedEditText mEditText;
private ImageButton mCancelButton;
@Mock
private SearchModeListener mSearchModeListener;
@@ -56,7 +56,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mEditText = new EditText(mContext);
+ mEditText = new ExtendedEditText(mContext);
mCancelButton = new ImageButton(mContext);
mController = new WidgetsSearchBarController(
mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
@@ -116,11 +116,10 @@
}
@Test
- public void cancelSearch_shouldInformSearchModeListenerToExitSearch() {
+ public void cancelSearch_shouldInformSearchModeListenerToClearResultsAndExitSearch() {
mCancelButton.performClick();
verify(mSearchModeListener).exitSearchMode();
- verifyNoMoreInteractions(mSearchModeListener);
}
@Test
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index e263c7a..95cdbdd 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -98,9 +98,6 @@
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
| TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
- // When these types of floating views are open, hide the taskbar hotseat and show the real one.
- public static final int TYPE_REPLACE_TASKBAR_WITH_HOTSEAT = TYPE_FOLDER | TYPE_ACTION_POPUP;
-
public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
& ~TYPE_ALL_APPS_EDU;
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 9d6af9f..5d41bb5 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -14,13 +14,16 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.SizeF;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
@@ -72,6 +75,7 @@
private LauncherAppWidgetHostView mWidgetView;
private CellLayout mCellLayout;
private DragLayer mDragLayer;
+ private ImageButton mReconfigureButton;
private Rect mWidgetPadding;
@@ -101,6 +105,8 @@
private int mRunningVInc;
private int mMinHSpan;
private int mMinVSpan;
+ private int mMaxHSpan;
+ private int mMaxVSpan;
private int mDeltaX;
private int mDeltaY;
private int mDeltaXAddOn;
@@ -139,10 +145,10 @@
protected void onFinishInflate() {
super.onFinishInflate();
- ViewGroup content = (ViewGroup) getChildAt(0);
- for (int i = 0; i < HANDLE_COUNT; i ++) {
- mDragHandles[i] = content.getChildAt(i);
- }
+ mDragHandles[INDEX_LEFT] = findViewById(R.id.widget_resize_left_handle);
+ mDragHandles[INDEX_TOP] = findViewById(R.id.widget_resize_top_handle);
+ mDragHandles[INDEX_RIGHT] = findViewById(R.id.widget_resize_right_handle);
+ mDragHandles[INDEX_BOTTOM] = findViewById(R.id.widget_resize_bottom_handle);
}
@Override
@@ -165,6 +171,15 @@
DragLayer dl = launcher.getDragLayer();
AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
.inflate(R.layout.app_widget_resize_frame, dl, false);
+ if (widget.hasEnforcedCornerRadius()) {
+ float enforcedCornerRadius = widget.getEnforcedCornerRadius();
+ ImageView imageView = frame.findViewById(R.id.widget_resize_frame);
+ Drawable d = imageView.getDrawable();
+ if (d instanceof GradientDrawable) {
+ GradientDrawable gd = (GradientDrawable) d.mutate();
+ gd.setCornerRadius(enforcedCornerRadius);
+ }
+ }
frame.setupForWidget(widget, cellLayout, dl);
((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
@@ -184,6 +199,8 @@
mMinHSpan = info.minSpanX;
mMinVSpan = info.minSpanY;
+ mMaxHSpan = info.maxSpanX;
+ mMaxVSpan = info.maxSpanY;
mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
widgetView.getAppWidgetInfo().provider, null);
@@ -196,6 +213,17 @@
mDragHandles[INDEX_RIGHT].setVisibility(GONE);
}
+ mReconfigureButton = (ImageButton) findViewById(R.id.widget_reconfigure_button);
+ if (info.isReconfigurable()) {
+ mReconfigureButton.setVisibility(VISIBLE);
+ mReconfigureButton.setOnClickListener(view -> mLauncher
+ .getAppWidgetHost()
+ .startConfigActivity(
+ mLauncher,
+ mWidgetView.getAppWidgetId(),
+ Launcher.REQUEST_RECONFIGURE_APPWIDGET));
+ }
+
// When we create the resize frame, we first mark all cells as unoccupied. The appropriate
// cells (same if not resized, or different) will be marked as occupied when the resize
// frame is dismissed.
@@ -315,7 +343,7 @@
// expandability.
mTempRange1.set(cellX, spanX + cellX);
int hSpanDelta = mTempRange1.applyDeltaAndBound(mLeftBorderActive, mRightBorderActive,
- hSpanInc, mMinHSpan, mCellLayout.getCountX(), mTempRange2);
+ hSpanInc, mMinHSpan, mMaxHSpan, mCellLayout.getCountX(), mTempRange2);
cellX = mTempRange2.start;
spanX = mTempRange2.size();
if (hSpanDelta != 0) {
@@ -324,7 +352,7 @@
mTempRange1.set(cellY, spanY + cellY);
int vSpanDelta = mTempRange1.applyDeltaAndBound(mTopBorderActive, mBottomBorderActive,
- vSpanInc, mMinVSpan, mCellLayout.getCountY(), mTempRange2);
+ vSpanInc, mMinVSpan, mMaxVSpan, mCellLayout.getCountY(), mTempRange2);
cellY = mTempRange2.start;
spanY = mTempRange2.size();
if (vSpanDelta != 0) {
@@ -567,6 +595,13 @@
return false;
}
+ private boolean isTouchOnReconfigureButton(MotionEvent ev) {
+ int xFrame = (int) ev.getX() - getLeft();
+ int yFrame = (int) ev.getY() - getTop();
+ mReconfigureButton.getHitRect(sTmpRect);
+ return sTmpRect.contains(xFrame, yFrame);
+ }
+
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
int action = ev.getAction();
@@ -594,6 +629,11 @@
if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
return true;
}
+ // Keep the resize frame open but let a click on the reconfigure button fall through to the
+ // button's OnClickListener.
+ if (isTouchOnReconfigureButton(ev)) {
+ return false;
+ }
close(false);
return false;
}
@@ -643,12 +683,15 @@
* @param minSize minimum size after with the moving edge should not be shifted any further.
* For eg, if delta = -3 when moving the endEdge brings the size to less than
* minSize, only delta = -2 will applied
+ * @param maxSize maximum size after with the moving edge should not be shifted any further.
+ * For eg, if delta = -3 when moving the endEdge brings the size to greater
+ * than maxSize, only delta = -2 will applied
* @param maxEnd The maximum value to the end edge (start edge is always restricted to 0)
* @return the amount of increase when endEdge was moves and the amount of decrease when
* the start edge was moved.
*/
public int applyDeltaAndBound(boolean moveStart, boolean moveEnd, int delta,
- int minSize, int maxEnd, IntRange out) {
+ int minSize, int maxSize, int maxEnd, IntRange out) {
applyDelta(moveStart, moveEnd, delta, out);
if (out.start < 0) {
out.start = 0;
@@ -663,6 +706,13 @@
out.end = out.start + minSize;
}
}
+ if (out.size() > maxSize) {
+ if (moveStart) {
+ out.start = out.end - maxSize;
+ } else if (moveEnd) {
+ out.end = out.start + maxSize;
+ }
+ }
return moveEnd ? out.size() - size() : size() - out.size();
}
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index e38ab74..9b350a1 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -329,6 +329,6 @@
* views
*/
public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) {
- return new DefaultSearchAdapterProvider(this);
+ return new DefaultSearchAdapterProvider(this, allapps);
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 2b58fb6..d333b49 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@@ -57,11 +56,12 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.PlaceHolderIconDrawable;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -92,12 +92,9 @@
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
private static final float HIGHLIGHT_SCALE = 1.16f;
-
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
- private static final int ICON_UPDATE_ANIMATION_DURATION = 375;
-
private float mScaleForReorderBounce = 1f;
protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -335,7 +332,7 @@
@UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
- FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
+ FastBitmapDrawable iconDrawable = info.newIcon(getContext());
mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
setIcon(iconDrawable);
@@ -933,7 +930,7 @@
private void resetIconScale() {
if (mIcon instanceof FastBitmapDrawable) {
- ((FastBitmapDrawable) mIcon).setScale(1f);
+ ((FastBitmapDrawable) mIcon).resetScale();
}
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 459b9a8..4740079 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -21,14 +21,9 @@
import static com.android.launcher3.LauncherState.NORMAL;
import android.animation.AnimatorSet;
-import android.animation.FloatArrayEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
@@ -45,10 +40,7 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.Thunk;
/**
* Implements a DropTarget.
@@ -72,6 +64,7 @@
private static final int[] sTempCords = new int[2];
private static final int DRAG_VIEW_DROP_DURATION = 285;
+ private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
public static final int TOOLTIP_DEFAULT = 0;
public static final int TOOLTIP_LEFT = 1;
@@ -89,9 +82,6 @@
/** An item must be dragged at least this many pixels before this drop target is enabled. */
private final int mDragDistanceThreshold;
- /** The paint applied to the drag view on hover */
- protected int mHoverColor = 0;
-
protected CharSequence mText;
protected ColorStateList mOriginalTextColor;
protected Drawable mDrawable;
@@ -101,7 +91,6 @@
private int mToolTipLocation;
private AnimatorSet mCurrentColorAnim;
- @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
public ButtonDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -177,8 +166,7 @@
mToolTip.showAsDropDown(this, x, y);
}
- d.dragView.setColor(mHoverColor);
- animateTextColor(mHoverColor);
+ d.dragView.setAlpha(DRAG_VIEW_HOVER_OVER_OPACITY);
if (d.stateAnnouncer != null) {
d.stateAnnouncer.cancel();
}
@@ -190,51 +178,15 @@
// Do nothing
}
- protected void resetHoverColor() {
- animateTextColor(mOriginalTextColor.getDefaultColor());
- }
-
- private void animateTextColor(int targetColor) {
- if (mCurrentColorAnim != null) {
- mCurrentColorAnim.cancel();
- }
-
- mCurrentColorAnim = new AnimatorSet();
- mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
-
- if (mSrcFilter == null) {
- mSrcFilter = new ColorMatrix();
- mDstFilter = new ColorMatrix();
- mCurrentFilter = new ColorMatrix();
- }
-
- int defaultTextColor = mOriginalTextColor.getDefaultColor();
- Themes.setColorChangeOnMatrix(defaultTextColor, getTextColor(), mSrcFilter);
- Themes.setColorChangeOnMatrix(defaultTextColor, targetColor, mDstFilter);
-
- ValueAnimator anim1 = ValueAnimator.ofObject(
- new FloatArrayEvaluator(mCurrentFilter.getArray()),
- mSrcFilter.getArray(), mDstFilter.getArray());
- anim1.addUpdateListener((anim) -> {
- mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
- invalidate();
- });
-
- mCurrentColorAnim.play(anim1);
- mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, TEXT_COLOR, targetColor));
- mCurrentColorAnim.start();
- }
-
@Override
public final void onDragExit(DragObject d) {
hideTooltip();
if (!d.dragComplete) {
d.dragView.setColor(0);
- resetHoverColor();
+ d.dragView.setAlpha(1f);
} else {
- // Restore the hover color
- d.dragView.setColor(mHoverColor);
+ d.dragView.setAlpha(DRAG_VIEW_HOVER_OVER_OPACITY);
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index b8833cf..7ece72e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -19,7 +19,6 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -60,6 +59,7 @@
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -180,6 +180,7 @@
private final ArrayList<View> mIntersectingViews = new ArrayList<>();
private final Rect mOccupiedRect = new Rect();
private final int[] mDirectionVector = new int[2];
+
final int[] mPreviousReorderDirection = new int[2];
private static final int INVALID_DIRECTION = -100;
@@ -209,15 +210,14 @@
setWillNotDraw(false);
setClipToPadding(false);
mActivity = ActivityContext.lookupContext(context);
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- DeviceProfile grid = mActivity.getDeviceProfile();
-
- mBorderSpacing = grid.cellLayoutBorderSpacingPx;
+ mBorderSpacing = deviceProfile.cellLayoutBorderSpacingPx;
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
- mCountX = grid.inv.numColumns;
- mCountY = grid.inv.numRows;
+ mCountX = deviceProfile.inv.numColumns;
+ mCountY = deviceProfile.inv.numRows;
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
@@ -234,7 +234,7 @@
mBackground.setCallback(this);
mBackground.setAlpha(0);
- mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);
+ mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
// Initialize the data structures used for the drag visualization.
mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
@@ -601,7 +601,7 @@
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
- if (ENABLE_FOUR_COLUMNS.get()) {
+ if (mActivity.getDeviceProfile().isScalableGrid) {
bubbleChild.setCenterVertically(mContainerType != HOTSEAT);
}
}
@@ -961,15 +961,18 @@
final int oldDragCellX = mDragCell[0];
final int oldDragCellY = mDragCell[1];
- if (outlineProvider == null || outlineProvider.generatedDragOutline == null) {
- return;
- }
-
- Bitmap dragOutline = outlineProvider.generatedDragOutline;
if (cellX != oldDragCellX || cellY != oldDragCellY) {
mDragCell[0] = cellX;
mDragCell[1] = cellY;
+ applyColorExtraction(dragObject, mDragCell, spanX, spanY);
+
+ if (outlineProvider == null || outlineProvider.generatedDragOutline == null) {
+ return;
+ }
+
+ Bitmap dragOutline = outlineProvider.generatedDragOutline;
+
final int oldIndex = mDragOutlineCurrent;
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
@@ -1011,6 +1014,22 @@
}
}
+ /** Applies the local color extraction to a dragging widget object. */
+ private void applyColorExtraction(DropTarget.DragObject dragObject, int[] targetCell, int spanX,
+ int spanY) {
+ // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
+ Drawable drawable = dragObject.dragView.getDrawable();
+ if (drawable instanceof AppWidgetHostViewDrawable) {
+ Workspace workspace =
+ Launcher.getLauncher(dragObject.dragView.getContext()).getWorkspace();
+ int screenId = workspace.getIdForScreen(this);
+ int pageId = workspace.getPageIndexForScreenId(screenId);
+ AppWidgetHostViewDrawable hostViewDrawable = ((AppWidgetHostViewDrawable) drawable);
+ cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
+ hostViewDrawable.getAppWidgetHostView().handleDrag(mTempRect, pageId);
+ }
+ }
+
@SuppressLint("StringFormatMatches")
public String getItemMoveDescription(int cellX, int cellY) {
if (mContainerType == HOTSEAT) {
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index cc119c9..e46aad2 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -53,9 +53,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- // Get the hover color
- mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
-
setDrawable(R.drawable.ic_remove_shadow);
}
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
index 4827f36..7c387b1 100644
--- a/src/com/android/launcher3/DevicePaddings.java
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -52,8 +52,8 @@
ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
- public DevicePaddings(Context context) {
- try (XmlResourceParser parser = context.getResources().getXml(R.xml.size_limits)) {
+ public DevicePaddings(Context context, int devicePaddingId) {
+ try (XmlResourceParser parser = context.getResources().getXml(devicePaddingId)) {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -94,16 +94,27 @@
if (workspaceTopPadding == null
|| workspaceBottomPadding == null
|| hotseatBottomPadding == null) {
- throw new RuntimeException("DevicePadding missing padding.");
+ if (Utilities.IS_DEBUG_DEVICE) {
+ throw new RuntimeException("DevicePadding missing padding.");
+ }
}
- mDevicePaddings.add(new DevicePadding(maxWidthPx, workspaceTopPadding,
- workspaceBottomPadding, hotseatBottomPadding));
+ DevicePadding dp = new DevicePadding(maxWidthPx, workspaceTopPadding,
+ workspaceBottomPadding, hotseatBottomPadding);
+ if (dp.isValid()) {
+ mDevicePaddings.add(dp);
+ } else {
+ Log.e(TAG, "Invalid device padding found.");
+ if (Utilities.IS_DEBUG_DEVICE) {
+ throw new RuntimeException("DevicePadding is invalid");
+ }
+ }
}
}
}
}
} catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failure parsing device padding layout.", e);
throw new RuntimeException(e);
}
@@ -128,6 +139,9 @@
*/
public static final class DevicePadding {
+ // One for each padding since they can each be off by 1 due to rounding errors.
+ private static final int ROUNDING_THRESHOLD_PX = 3;
+
private final int maxEmptySpacePx;
private final PaddingFormula workspaceTopPadding;
private final PaddingFormula workspaceBottomPadding;
@@ -143,6 +157,10 @@
this.hotseatBottomPadding = hotseatBottomPadding;
}
+ public int getMaxEmptySpacePx() {
+ return maxEmptySpacePx;
+ }
+
public int getWorkspaceTopPadding(int extraSpacePx) {
return workspaceTopPadding.calculate(extraSpacePx);
}
@@ -154,6 +172,22 @@
public int getHotseatBottomPadding(int extraSpacePx) {
return hotseatBottomPadding.calculate(extraSpacePx);
}
+
+ public boolean isValid() {
+ int workspaceTopPadding = getWorkspaceTopPadding(maxEmptySpacePx);
+ int workspaceBottomPadding = getWorkspaceBottomPadding(maxEmptySpacePx);
+ int hotseatBottomPadding = getHotseatBottomPadding(maxEmptySpacePx);
+ int sum = workspaceTopPadding + workspaceBottomPadding + hotseatBottomPadding;
+ int diff = Math.abs(sum - maxEmptySpacePx);
+ if (DEBUG) {
+ Log.d(TAG, "isValid: workspaceTopPadding=" + workspaceTopPadding
+ + ", workspaceBottomPadding=" + workspaceBottomPadding
+ + ", hotseatBottomPadding=" + hotseatBottomPadding
+ + ", sum=" + sum
+ + ", diff=" + diff);
+ }
+ return diff <= ROUNDING_THRESHOLD_PX;
+ }
}
/**
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 90cc384..1ce5f4d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.ResourceUtils.pxFromDp;
+import static com.android.launcher3.Utilities.dpiFromPx;
import android.content.Context;
import android.content.res.Configuration;
@@ -38,6 +39,8 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.WindowBounds;
+import java.io.PrintWriter;
+
public class DeviceProfile {
private static final float TABLET_MIN_DPS = 600;
@@ -91,14 +94,17 @@
public float workspaceSpringLoadShrinkFactor;
public final int workspaceSpringLoadedBottomSpace;
+ private final int extraSpace;
public int workspaceTopPadding;
public int workspaceBottomPadding;
+ public int extraHotseatBottomPadding;
// Workspace page indicator
public final int workspacePageIndicatorHeight;
private final int mWorkspacePageIndicatorOverlapWorkspace;
// Workspace icons
+ public float iconScale;
public int iconSizePx;
public int iconTextSizePx;
public int iconDrawablePaddingPx;
@@ -109,7 +115,6 @@
public int workspaceCellPaddingXPx;
public int cellYPaddingPx;
- public int cellYPaddingOriginalPx;
// Folder
public float folderLabelTextScale;
@@ -147,6 +152,11 @@
public int allAppsIconDrawablePaddingPx;
public float allAppsIconTextSizePx;
+ // Overview
+ public int overviewTaskMarginPx;
+ public int overviewTaskIconSizePx;
+ public int overviewTaskThumbnailTopMarginPx;
+
// Widgets
public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
@@ -199,8 +209,7 @@
mInfo = info;
// Constants from resources
- float swDPs = Utilities.dpiFromPx(
- Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
+ float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation);
// Tablet UI is built with assumption that simulated landscape is disabled.
isTablet = allowRotation && swDPs >= TABLET_MIN_DPS;
@@ -251,7 +260,12 @@
int cellLayoutPadding = isScalableGrid
? 0
: res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
- if (isLandscape) {
+
+ if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) {
+ cellLayoutPaddingLeftRightPx =
+ res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
+ cellLayoutBottomPaddingPx = 0;
+ } else if (isLandscape) {
cellLayoutPaddingLeftRightPx = 0;
cellLayoutBottomPaddingPx = cellLayoutPadding;
} else {
@@ -289,23 +303,37 @@
: (hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx
+ (isScalableGrid ? 0 : hotseatExtraVerticalSize)));
- // Calculate all of the remaining variables.
- int extraSpace = updateAvailableDimensions(res);
- // Now that we have all of the variables calculated, we can tune certain sizes.
- if (isScalableGrid) {
- DevicePadding padding = inv.devicePaddings.getDevicePadding(extraSpace);
- workspaceTopPadding = padding.getWorkspaceTopPadding(extraSpace);
- workspaceBottomPadding = padding.getWorkspaceBottomPadding(extraSpace);
+ overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
+ overviewTaskIconSizePx =
+ isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() ? res.getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_size_grid) : res.getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_size);
+ overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
- float hotseatBarBottomPadding = padding.getHotseatBottomPadding(extraSpace);
- hotseatBarSizePx += hotseatBarBottomPadding;
- hotseatBarBottomPaddingPx += hotseatBarBottomPadding;
+ // Calculate all of the remaining variables.
+ extraSpace = updateAvailableDimensions(res);
+ // Now that we have all of the variables calculated, we can tune certain sizes.
+ if (isScalableGrid && inv.devicePaddings != null) {
+ // Paddings were created assuming no scaling, so we first unscale the extra space.
+ int unscaledExtraSpace = (int) (extraSpace / iconScale);
+ DevicePadding padding = inv.devicePaddings.getDevicePadding(unscaledExtraSpace);
+
+ int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
+ int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
+ int paddingHotseatBottom = padding.getHotseatBottomPadding(unscaledExtraSpace);
+
+ workspaceTopPadding = Math.round(paddingWorkspaceTop * iconScale);
+ workspaceBottomPadding = Math.round(paddingWorkspaceBottom * iconScale);
+ extraHotseatBottomPadding = Math.round(paddingHotseatBottom * iconScale);
+
+ hotseatBarSizePx += extraHotseatBottomPadding;
+ hotseatBarBottomPaddingPx += extraHotseatBottomPadding;
} else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
// We increase the hotseat size when there is extra space.
// ie. For a display with a large aspect ratio, we can keep the icons on the workspace
// in portrait mode closer together by adding more height to the hotseat.
// Note: This calculation was created after noticing a pattern in the design spec.
- extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
+ int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
- workspacePageIndicatorHeight;
hotseatBarSizePx += extraSpace;
hotseatBarBottomPaddingPx += extraSpace;
@@ -470,6 +498,8 @@
* hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
*/
public void updateIconSize(float scale, Resources res) {
+ iconScale = scale;
+
// Workspace
final boolean isVerticalLayout = isVerticalBarLayout();
float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
@@ -660,6 +690,10 @@
- (2 * inv.numRows * cellHeightPx) - hotseatVerticalPadding);
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
+
+ if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) {
+ padding.set(0, padding.top, 0, padding.bottom);
+ }
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes
padding.set(desiredWorkspaceLeftRightMarginPx,
@@ -785,6 +819,100 @@
}
}
+ private String pxToDpStr(String name, float value) {
+ return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mInfo.metrics) + "dp)";
+ }
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + "DeviceProfile:");
+ writer.println(prefix + "\t1 dp = " + mInfo.metrics.density + " px");
+
+ writer.println(prefix + "\tisTablet:" + isTablet);
+ writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
+ writer.println(prefix + "\tisPhone:" + isPhone);
+ writer.println(prefix + "\ttransposeLayoutWithOrientation:"
+ + transposeLayoutWithOrientation);
+
+ writer.println(prefix + "\tisLandscape:" + isLandscape);
+ writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
+
+ writer.println(prefix + pxToDpStr("windowX", windowX));
+ writer.println(prefix + pxToDpStr("windowY", windowY));
+ writer.println(prefix + pxToDpStr("widthPx", widthPx));
+ writer.println(prefix + pxToDpStr("heightPx", heightPx));
+
+ writer.println(prefix + pxToDpStr("availableWidthPx", availableWidthPx));
+ writer.println(prefix + pxToDpStr("availableHeightPx", availableHeightPx));
+
+ writer.println(prefix + "\taspectRatio:" + aspectRatio);
+
+ writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
+
+ writer.println(prefix + "\tinv.minCellWidth:" + inv.minCellWidth + "dp");
+ writer.println(prefix + "\tinv.minCellHeight:" + inv.minCellHeight + "dp");
+
+ writer.println(prefix + pxToDpStr("cellWidthPx", cellWidthPx));
+ writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
+
+ writer.println(prefix + pxToDpStr("getCellSize().x", getCellSize().x));
+ writer.println(prefix + pxToDpStr("getCellSize().y", getCellSize().y));
+
+ writer.println(prefix + "\tinv.iconSize:" + inv.iconSize + "dp");
+ writer.println(prefix + pxToDpStr("iconSizePx", iconSizePx));
+ writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
+ writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx));
+
+ writer.println(prefix + pxToDpStr("folderCellWidthPx", folderCellWidthPx));
+ writer.println(prefix + pxToDpStr("folderCellHeightPx", folderCellHeightPx));
+ writer.println(prefix + pxToDpStr("folderChildIconSizePx", folderChildIconSizePx));
+ writer.println(prefix + pxToDpStr("folderChildTextSizePx", folderChildTextSizePx));
+ writer.println(prefix + pxToDpStr("folderChildDrawablePaddingPx",
+ folderChildDrawablePaddingPx));
+
+ writer.println(prefix + pxToDpStr("cellLayoutBorderSpacingPx",
+ cellLayoutBorderSpacingPx));
+ writer.println(prefix + pxToDpStr("desiredWorkspaceLeftRightMarginPx",
+ desiredWorkspaceLeftRightMarginPx));
+
+ writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
+ writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
+ writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
+ allAppsIconDrawablePaddingPx));
+ writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
+
+ writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
+ writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
+ writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
+ writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
+ writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
+ hotseatBarSidePaddingStartPx));
+ writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
+ hotseatBarSidePaddingEndPx));
+
+ writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
+
+ writer.println(prefix + pxToDpStr("taskbarSize", taskbarSize));
+ writer.println(prefix + pxToDpStr("nonOverlappingTaskbarInset",
+ nonOverlappingTaskbarInset));
+
+ writer.println(prefix + pxToDpStr("workspacePadding.left", workspacePadding.left));
+ writer.println(prefix + pxToDpStr("workspacePadding.top", workspacePadding.top));
+ writer.println(prefix + pxToDpStr("workspacePadding.right", workspacePadding.right));
+ writer.println(prefix + pxToDpStr("workspacePadding.bottom", workspacePadding.bottom));
+
+ writer.println(prefix + pxToDpStr("scaleToFit", iconScale));
+ writer.println(prefix + pxToDpStr("extraSpace", extraSpace));
+
+ if (inv.devicePaddings != null) {
+ int unscaledExtraSpace = (int) (extraSpace / iconScale);
+ writer.println(prefix + pxToDpStr("maxEmptySpace",
+ inv.devicePaddings.getDevicePadding(unscaledExtraSpace).getMaxEmptySpacePx()));
+ }
+ writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
+ writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
+ writer.println(prefix + pxToDpStr("extraHotseatBottomPadding", extraHotseatBottomPadding));
+ }
+
private static Context getContext(Context c, Info info, int orientation) {
Configuration config = new Configuration(c.getResources().getConfiguration());
config.orientation = orientation;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
deleted file mode 100644
index b1fe4a2..0000000
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.Property;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.graphics.PlaceHolderIconDrawable;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.util.Themes;
-
-
-public class FastBitmapDrawable extends Drawable {
-
- private static final float PRESSED_SCALE = 1.1f;
-
- private static final float DISABLED_DESATURATION = 1f;
- private static final float DISABLED_BRIGHTNESS = 0.5f;
-
- public static final int CLICK_FEEDBACK_DURATION = 200;
-
- private static ColorFilter sDisabledFColorFilter;
-
- protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
- protected Bitmap mBitmap;
- protected final int mIconColor;
-
- @Nullable private ColorFilter mColorFilter;
-
- private boolean mIsPressed;
- private boolean mIsDisabled;
- private float mDisabledAlpha = 1f;
-
- // Animator and properties for the fast bitmap drawable's scale
- private static final Property<FastBitmapDrawable, Float> SCALE
- = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") {
- @Override
- public Float get(FastBitmapDrawable fastBitmapDrawable) {
- return fastBitmapDrawable.mScale;
- }
-
- @Override
- public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
- fastBitmapDrawable.mScale = value;
- fastBitmapDrawable.invalidateSelf();
- }
- };
- private ObjectAnimator mScaleAnimation;
- private float mScale = 1;
-
- private int mAlpha = 255;
-
- public FastBitmapDrawable(Bitmap b) {
- this(b, Color.TRANSPARENT);
- }
-
- public FastBitmapDrawable(BitmapInfo info) {
- this(info.icon, info.color);
- }
-
- protected FastBitmapDrawable(Bitmap b, int iconColor) {
- this(b, iconColor, false);
- }
-
- protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) {
- mBitmap = b;
- mIconColor = iconColor;
- setFilterBitmap(true);
- setIsDisabled(isDisabled);
- }
-
- @Override
- public final void draw(Canvas canvas) {
- if (mScale != 1f) {
- int count = canvas.save();
- Rect bounds = getBounds();
- canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
- drawInternal(canvas, bounds);
- canvas.restoreToCount(count);
- } else {
- drawInternal(canvas, getBounds());
- }
- }
-
- protected void drawInternal(Canvas canvas, Rect bounds) {
- canvas.drawBitmap(mBitmap, null, bounds, mPaint);
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- mColorFilter = cf;
- updateFilter();
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public void setAlpha(int alpha) {
- if (mAlpha != alpha) {
- mAlpha = alpha;
- mPaint.setAlpha(alpha);
- invalidateSelf();
- }
- }
-
- @Override
- public void setFilterBitmap(boolean filterBitmap) {
- mPaint.setFilterBitmap(filterBitmap);
- mPaint.setAntiAlias(filterBitmap);
- }
-
- public int getAlpha() {
- return mAlpha;
- }
-
- public void setScale(float scale) {
- if (mScaleAnimation != null) {
- mScaleAnimation.cancel();
- mScaleAnimation = null;
- }
- mScale = scale;
- invalidateSelf();
- }
-
- public float getAnimatedScale() {
- return mScaleAnimation == null ? 1 : mScale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return mBitmap.getWidth();
- }
-
- @Override
- public int getIntrinsicHeight() {
- return mBitmap.getHeight();
- }
-
- @Override
- public int getMinimumWidth() {
- return getBounds().width();
- }
-
- @Override
- public int getMinimumHeight() {
- return getBounds().height();
- }
-
- @Override
- public boolean isStateful() {
- return true;
- }
-
- @Override
- public ColorFilter getColorFilter() {
- return mPaint.getColorFilter();
- }
-
- @Override
- protected boolean onStateChange(int[] state) {
- boolean isPressed = false;
- for (int s : state) {
- if (s == android.R.attr.state_pressed) {
- isPressed = true;
- break;
- }
- }
- if (mIsPressed != isPressed) {
- mIsPressed = isPressed;
-
- if (mScaleAnimation != null) {
- mScaleAnimation.cancel();
- mScaleAnimation = null;
- }
-
- if (mIsPressed) {
- // Animate when going to pressed state
- mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
- mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
- mScaleAnimation.setInterpolator(ACCEL);
- mScaleAnimation.start();
- } else {
- if (isVisible()) {
- mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
- mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
- mScaleAnimation.setInterpolator(DEACCEL);
- mScaleAnimation.start();
- } else {
- mScale = 1f;
- invalidateSelf();
- }
- }
- return true;
- }
- return false;
- }
-
- public void setIsDisabled(boolean isDisabled) {
- if (mIsDisabled != isDisabled) {
- mIsDisabled = isDisabled;
- updateFilter();
- }
- }
-
- protected boolean isDisabled() {
- return mIsDisabled;
- }
-
- private ColorFilter getDisabledColorFilter() {
- if (sDisabledFColorFilter == null) {
- ColorMatrix tempBrightnessMatrix = new ColorMatrix();
- ColorMatrix tempFilterMatrix = new ColorMatrix();
-
- tempFilterMatrix.setSaturation(1f - DISABLED_DESATURATION);
- float scale = 1 - DISABLED_BRIGHTNESS;
- int brightnessI = (int) (255 * DISABLED_BRIGHTNESS);
- float[] mat = tempBrightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = brightnessI;
- mat[9] = brightnessI;
- mat[14] = brightnessI;
- mat[18] = mDisabledAlpha;
- tempFilterMatrix.preConcat(tempBrightnessMatrix);
- sDisabledFColorFilter = new ColorMatrixColorFilter(tempFilterMatrix);
- }
- return sDisabledFColorFilter;
- }
-
- /**
- * Updates the paint to reflect the current brightness and saturation.
- */
- protected void updateFilter() {
- mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter);
- invalidateSelf();
- }
-
- @Override
- public ConstantState getConstantState() {
- return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled);
- }
-
- protected static class FastBitmapConstantState extends ConstantState {
- protected final Bitmap mBitmap;
- protected final int mIconColor;
- protected final boolean mIsDisabled;
-
- public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) {
- mBitmap = bitmap;
- mIconColor = color;
- mIsDisabled = isDisabled;
- }
-
- @Override
- public FastBitmapDrawable newDrawable() {
- return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
- }
-
- @Override
- public int getChangingConfigurations() {
- return 0;
- }
- }
-
- /**
- * Interface to be implemented by custom {@link BitmapInfo} to handle drawable construction
- */
- public interface Factory {
-
- /**
- * Called to create a new drawable
- */
- FastBitmapDrawable newDrawable();
- }
-
- /**
- * Returns a FastBitmapDrawable with the icon.
- */
- public static FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
- FastBitmapDrawable drawable = newIcon(context, info.bitmap);
- drawable.setIsDisabled(info.isDisabled());
- return drawable;
- }
-
- /**
- * Creates a drawable for the provided BitmapInfo
- */
- public static FastBitmapDrawable newIcon(Context context, BitmapInfo info) {
- final FastBitmapDrawable drawable;
- if (info instanceof Factory) {
- drawable = ((Factory) info).newDrawable();
- } else if (info.isLowRes()) {
- drawable = new PlaceHolderIconDrawable(info, context);
- } else {
- drawable = new FastBitmapDrawable(info);
- }
- drawable.mDisabledAlpha = Themes.getFloat(context, R.attr.disabledIconAlpha, 1f);
- return drawable;
- }
-}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index e5b75c1..15e12d6 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -20,6 +20,7 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
@@ -28,9 +29,7 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.MultiValueAlpha;
-
+import java.util.Arrays;
import java.util.function.Consumer;
/**
@@ -38,9 +37,8 @@
*/
public class Hotseat extends CellLayout implements Insettable {
- private static final int ALPHA_INDEX_STATE = 0;
- private static final int ALPHA_INDEX_REPLACE_TASKBAR = 1;
- private static final int NUM_ALPHA_CHANNELS = 2;
+ // Ratio of empty space, qsb should take up to appear visually centered.
+ public static final float QSB_CENTER_FACTOR = .325f;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mHasVerticalHotseat;
@@ -49,7 +47,11 @@
@Nullable
private Consumer<Boolean> mOnVisibilityAggregatedCallback;
- private final MultiValueAlpha mMultiValueAlpha;
+ private final View mQsb;
+ private final int mQsbHeight;
+
+ private final View mTaskbarView;
+ private final int mTaskbarViewHeight;
public Hotseat(Context context) {
this(context, null);
@@ -61,8 +63,15 @@
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS, MultiValueAlpha.Mode.MAX);
- mMultiValueAlpha.setUpdateVisibility(true);
+
+ mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+ mQsbHeight = mQsb.getLayoutParams().height;
+ addView(mQsb);
+
+ mTaskbarView = LayoutInflater.from(context).inflate(R.layout.taskbar_view, this, false);
+ mTaskbarViewHeight = mTaskbarView.getLayoutParams().height;
+ // We want taskbar in the back so its background applies to Hotseat as well.
+ addView(mTaskbarView, 0);
}
/**
@@ -86,7 +95,7 @@
if (hasVerticalHotseat) {
setGridSize(1, idp.numHotseatIcons);
} else {
- setGridSize(idp.numHotseatIcons, FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 2 : 1);
+ setGridSize(idp.numHotseatIcons, 1);
}
showInlineQsb();
}
@@ -97,6 +106,7 @@
DeviceProfile grid = mActivity.getDeviceProfile();
if (grid.isVerticalBarLayout()) {
+ mQsb.setVisibility(View.GONE);
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
if (grid.isSeascape()) {
lp.gravity = Gravity.LEFT;
@@ -106,21 +116,20 @@
lp.width = grid.hotseatBarSizePx + insets.right;
}
} else {
+ mQsb.setVisibility(View.VISIBLE);
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = grid.isTaskbarPresent
- ? grid.taskbarSize
- : grid.hotseatBarSizePx + insets.bottom;
+ lp.height = (grid.isTaskbarPresent
+ ? grid.workspacePadding.bottom
+ : grid.hotseatBarSizePx)
+ + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
}
+
if (!grid.isTaskbarPresent) {
// When taskbar is present, we set the padding separately to ensure a seamless visual
// handoff between taskbar and hotseat during drag and drop.
Rect padding = grid.getHotseatLayoutPadding();
- int paddingBottom = padding.bottom;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !grid.isVerticalBarLayout()) {
- paddingBottom -= grid.hotseatBarBottomPaddingPx;
- }
- setPadding(padding.left, padding.top, padding.right, paddingBottom);
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
setLayoutParams(lp);
@@ -177,18 +186,68 @@
//Does nothing
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = getShortcutsAndWidgets().getMeasuredWidth();
+ mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
+ mTaskbarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mTaskbarViewHeight, MeasureSpec.EXACTLY));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ int qsbWidth = mQsb.getMeasuredWidth();
+ int left = (r - l - qsbWidth) / 2;
+ int right = left + qsbWidth;
+
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ int freeSpace = dp.isTaskbarPresent
+ ? dp.workspacePadding.bottom
+ : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
+ int bottom = b - t
+ - (int) (freeSpace * QSB_CENTER_FACTOR)
+ - (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
+ int top = bottom - mQsbHeight;
+ mQsb.layout(left, top, right, bottom);
+
+ int taskbarWidth = mTaskbarView.getMeasuredWidth();
+ left = (r - l - taskbarWidth) / 2;
+ right = left + taskbarWidth;
+ bottom = b - t;
+ top = bottom - mTaskbarViewHeight;
+ mTaskbarView.layout(left, top, right, bottom);
+ }
+
/**
* Returns the first View for which the given itemOperator returns true, or null.
*/
public View getFirstItemMatch(Workspace.ItemOperator itemOperator) {
- return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator);
+ return mWorkspace.getFirstMatch(Arrays.asList(this), itemOperator);
}
- public MultiValueAlpha.AlphaProperty getStateAlpha() {
- return mMultiValueAlpha.getProperty(ALPHA_INDEX_STATE);
+ /**
+ * Sets the alpha value of just our ShortcutAndWidgetContainer.
+ */
+ public void setIconsAlpha(float alpha) {
+ getShortcutsAndWidgets().setAlpha(alpha);
}
- public MultiValueAlpha.AlphaProperty getReplaceTaskbarAlpha() {
- return mMultiValueAlpha.getProperty(ALPHA_INDEX_REPLACE_TASKBAR);
+ /**
+ * Returns the QSB inside hotseat
+ */
+ public View getQsb() {
+ return mQsb;
+ }
+
+ /**
+ * Returns the Taskbar inside hotseat
+ */
+ public View getTaskbarView() {
+ return mTaskbarView;
}
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index bb60557..754e988 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
@@ -131,6 +132,7 @@
* Do not query directly. see {@link DeviceProfile#isScalableGrid}.
*/
protected boolean isScalable;
+ public int devicePaddingId;
public String dbFile;
public int defaultLayoutId;
@@ -139,7 +141,7 @@
public DeviceProfile landscapeProfile;
public DeviceProfile portraitProfile;
- public DevicePaddings devicePaddings;
+ @Nullable public DevicePaddings devicePaddings;
public Point defaultWallpaperSize;
public Rect defaultWidgetPadding;
@@ -164,6 +166,7 @@
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
isScalable = p.isScalable;
+ devicePaddingId = p.devicePaddingId;
minCellHeight = p.minCellHeight;
minCellWidth = p.minCellWidth;
borderSpacing = p.borderSpacing;
@@ -224,17 +227,24 @@
.add(myDisplayOption);
result.iconSize = defaultDisplayOption.iconSize;
result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
- result.allAppsIconSize = Math.min(
- defaultDisplayOption.allAppsIconSize, myDisplayOption.allAppsIconSize);
+ if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
+ result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
+ result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
+ } else {
+ result.allAppsIconSize = myDisplayOption.allAppsIconSize;
+ result.numAllAppsColumns = myDisplayOption.numAllAppsColumns;
+ }
result.minCellHeight = defaultDisplayOption.minCellHeight;
result.minCellWidth = defaultDisplayOption.minCellWidth;
result.borderSpacing = defaultDisplayOption.borderSpacing;
- devicePaddings = new DevicePaddings(context);
initGrid(context, myInfo, result);
}
public static String getCurrentGridName(Context context) {
+ if (ENABLE_TWO_PANEL_HOME.get()) {
+ return ENABLE_TWO_PANEL_HOME.key;
+ }
if (ENABLE_FOUR_COLUMNS.get()) {
return ENABLE_FOUR_COLUMNS.key;
}
@@ -258,7 +268,6 @@
ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
- devicePaddings = new DevicePaddings(context);
initGrid(context, displayInfo, displayOption);
return displayOption.grid.name;
}
@@ -274,8 +283,8 @@
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
- numAllAppsColumns = closestProfile.numAllAppsColumns;
isScalable = closestProfile.isScalable;
+ devicePaddingId = closestProfile.devicePaddingId;
mExtraAttrs = closestProfile.extraAttrs;
@@ -289,6 +298,7 @@
minCellHeight = displayOption.minCellHeight;
minCellWidth = displayOption.minCellWidth;
borderSpacing = displayOption.borderSpacing;
+ numAllAppsColumns = Math.round(displayOption.numAllAppsColumns);
if (Utilities.isGridOptionsEnabled(context)) {
allAppsIconSize = displayOption.allAppsIconSize;
@@ -298,6 +308,10 @@
allAppsIconTextSize = iconTextSize;
}
+ if (devicePaddingId != 0) {
+ devicePaddings = new DevicePaddings(context, devicePaddingId);
+ }
+
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
@@ -605,12 +619,14 @@
private final int numHotseatIcons;
private final String dbFile;
- private final int numAllAppsColumns;
private final int defaultLayoutId;
private final int demoModeLayoutId;
private final boolean isScalable;
+ private final int devicePaddingId;
+
+ public final boolean visible;
private final SparseArray<TypedValue> extraAttrs;
@@ -632,11 +648,13 @@
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
- numAllAppsColumns = a.getInt(
- R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
isScalable = a.getBoolean(
R.styleable.GridDisplayOption_isScalable, false);
+ devicePaddingId = a.getResourceId(
+ R.styleable.GridDisplayOption_devicePaddingId, 0);
+
+ visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true);
a.recycle();
@@ -652,6 +670,7 @@
private final float minHeightDps;
private final boolean canBeDefault;
+ private float numAllAppsColumns;
private float minCellHeight;
private float minCellWidth;
private float borderSpacing;
@@ -672,6 +691,8 @@
minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
canBeDefault = a.getBoolean(
R.styleable.ProfileDisplayOption_canBeDefault, false);
+ numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
+ grid.numColumns);
minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
@@ -698,12 +719,14 @@
minWidthDps = 0;
minHeightDps = 0;
canBeDefault = false;
+ numAllAppsColumns = 0;
minCellHeight = 0;
minCellWidth = 0;
borderSpacing = 0;
}
private DisplayOption multiply(float w) {
+ numAllAppsColumns *= w;
iconSize *= w;
landscapeIconSize *= w;
allAppsIconSize *= w;
@@ -716,6 +739,7 @@
}
private DisplayOption add(DisplayOption p) {
+ numAllAppsColumns += p.numAllAppsColumns;
iconSize += p.iconSize;
landscapeIconSize += p.landscapeIconSize;
allAppsIconSize += p.allAppsIconSize;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c57f621..5eba399 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1781,9 +1781,7 @@
@Override
public Rect getFolderBoundingBox() {
// We need to bound the folder to the currently visible workspace area
- Rect folderBoundingBox = new Rect();
- getWorkspace().getPageAreaRelativeToDragLayer(folderBoundingBox);
- return folderBoundingBox;
+ return getWorkspace().getPageAreaRelativeToDragLayer();
}
@Override
@@ -2634,6 +2632,7 @@
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
mPopupDataProvider.dump(prefix, writer);
+ mDeviceProfile.dump(prefix, writer);
try {
FileLog.flushAll(writer);
@@ -2762,6 +2761,13 @@
return new float[] {NO_SCALE, NO_OFFSET};
}
+ /**
+ * @see LauncherState#getTaskbarScale(Launcher)
+ */
+ public float getNormalTaskbarScale() {
+ return 1f;
+ }
+
public static Launcher getLauncher(Context context) {
return fromContext(context);
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 57d7600..ccc023a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,8 +17,11 @@
package com.android.launcher3;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
-import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
import android.content.ComponentName;
import android.content.Context;
@@ -37,10 +40,10 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -122,6 +125,34 @@
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
+
+ // b/175329686 Temporary logic to gracefully migrate group of users to the new 4x5 grid.
+ String gridName = InvariantDeviceProfile.getCurrentGridName(context);
+ if (ENABLE_FOUR_COLUMNS.get()
+ || "reasonable".equals(gridName)
+ || ENABLE_FOUR_COLUMNS.key.equals(gridName)) {
+ // Reset flag and remove it from developer options to prevent it from being enabled
+ // again.
+ ENABLE_FOUR_COLUMNS.reset(context);
+ FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS);
+
+ // Force migration code to run
+ Utilities.getPrefs(context).edit()
+ .remove(KEY_MIGRATION_SRC_HOTSEAT_COUNT)
+ .remove(KEY_MIGRATION_SRC_WORKSPACE_SIZE)
+ .apply();
+
+ // We make an empty call here to ensure the database is created with the old IDP grid,
+ // so that when we set the new grid the migration can proceeds as expected.
+ LauncherSettings.Settings.call(context.getContentResolver(), "");
+
+ String newGridName = "practical";
+ Utilities.getPrefs(mContext).edit().putString("idp_grid_name", newGridName).commit();
+ mInvariantDeviceProfile.setCurrentGrid(context, "practical");
+ } else {
+ FeatureFlags.removeFlag(ENABLE_FOUR_COLUMNS);
+ }
+
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 25afb55..6c0daa4 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -15,6 +15,7 @@
private static final String XML = ".xml";
public static final String LAUNCHER_DB = "launcher.db";
+ public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db";
public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
@@ -30,6 +31,7 @@
public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
LAUNCHER_DB,
+ LAUNCHER_4_BY_5_DB,
LAUNCHER_4_BY_4_DB,
LAUNCHER_3_BY_3_DB,
LAUNCHER_2_BY_2_DB,
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index aa97450..1003958 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -17,9 +17,11 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
@@ -30,7 +32,6 @@
import android.content.Context;
import android.view.animation.Interpolator;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
@@ -52,20 +53,13 @@
*/
public static final int NONE = 0;
public static final int HOTSEAT_ICONS = 1 << 0;
- public static final int HOTSEAT_SEARCH_BOX = 1 << 1;
- public static final int ALL_APPS_HEADER = 1 << 2;
- public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
- public static final int ALL_APPS_CONTENT = 1 << 4;
- public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
- public static final int OVERVIEW_ACTIONS = 1 << 6;
- public static final int TASKBAR = 1 << 7;
- public static final int CLEAR_ALL_BUTTON = 1 << 8;
- public static final int WORKSPACE_PAGE_INDICATOR = 1 << 9;
- public static final int SPLIT_PLACHOLDER_VIEW = 1 << 10;
-
- /** Mask of all the items that are contained in the apps view. */
- public static final int APPS_VIEW_ITEM_MASK =
- HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+ public static final int ALL_APPS_CONTENT = 1 << 1;
+ public static final int VERTICAL_SWIPE_INDICATOR = 1 << 2;
+ public static final int OVERVIEW_ACTIONS = 1 << 3;
+ public static final int TASKBAR = 1 << 4;
+ public static final int CLEAR_ALL_BUTTON = 1 << 5;
+ public static final int WORKSPACE_PAGE_INDICATOR = 1 << 6;
+ public static final int SPLIT_PLACHOLDER_VIEW = 1 << 7;
// Flag indicating workspace has multiple pages visible.
public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -97,7 +91,7 @@
}
};
- private static final LauncherState[] sAllStates = new LauncherState[9];
+ private static final LauncherState[] sAllStates = new LauncherState[10];
/**
* TODO: Create a separate class for NORMAL state.
@@ -120,6 +114,8 @@
SPRING_LOADED_STATE_ORDINAL);
public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
+ public static final LauncherState HINT_STATE_TWO_BUTTON = new HintState(
+ HINT_STATE_TWO_BUTTON_ORDINAL, LAUNCHER_STATE_OVERVIEW);
public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
public static final LauncherState OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
@@ -184,8 +180,8 @@
return launcher.getNormalOverviewScaleAndOffset();
}
- public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
- return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+ public float getTaskbarScale(Launcher launcher) {
+ return launcher.getNormalTaskbarScale();
}
public float getOverviewFullscreenProgress() {
@@ -193,15 +189,7 @@
}
public int getVisibleElements(Launcher launcher) {
- DeviceProfile deviceProfile = launcher.getDeviceProfile();
- int flags = WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
- if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !deviceProfile.isVerticalBarLayout()) {
- flags |= HOTSEAT_SEARCH_BOX;
- }
- if (!deviceProfile.isTaskbarPresent) {
- flags |= HOTSEAT_ICONS;
- }
- return flags;
+ return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
}
/**
@@ -238,13 +226,6 @@
}
/**
- * For this state, whether tasks should layout as a grid rather than a list.
- */
- public boolean displayOverviewTasksAsGrid(Launcher launcher) {
- return false;
- }
-
- /**
* For this state, how much additional vertical translation there should be for each of the
* child TaskViews.
*/
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 50f1e44..01f7c71 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -63,6 +63,7 @@
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
+import java.util.List;
/**
* An abstraction of the original Workspace which supports browsing through a
@@ -282,7 +283,47 @@
private int validateNewPage(int newPage) {
newPage = ensureWithinScrollBounds(newPage);
// Ensure that it is clamped by the actual set of children in all cases
- return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+ newPage = Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+
+ if (getPanelCount() > 1) {
+ // Always return left panel as new page
+ newPage = getLeftmostVisiblePageForIndex(newPage);
+ }
+ return newPage;
+ }
+
+ private int getLeftmostVisiblePageForIndex(int pageIndex) {
+ int panelCount = getPanelCount();
+ return (pageIndex / panelCount) * panelCount;
+ }
+
+ /**
+ * Returns the number of pages that are shown at the same time.
+ */
+ protected int getPanelCount() {
+ return 1;
+ }
+
+ /**
+ * Returns the currently visible pages.
+ */
+ public Iterable<View> getVisiblePages() {
+ int panelCount = getPanelCount();
+ List<View> visiblePages = new ArrayList<>(panelCount);
+ for (int i = mCurrentPage; i < mCurrentPage + panelCount; i++) {
+ View page = getPageAt(i);
+ if (page != null) {
+ visiblePages.add(page);
+ }
+ }
+ return visiblePages;
+ }
+
+ /**
+ * Returns true if the view is on one of the current pages, false otherwise.
+ */
+ public boolean isVisible(View child) {
+ return getLeftmostVisiblePageForIndex(indexOfChild(child)) == mCurrentPage;
}
/**
@@ -548,6 +589,10 @@
super.forceLayout();
}
+ private int getPageWidthSize(int widthSize) {
+ return (widthSize - mInsets.left - mInsets.right) / getPanelCount();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() == 0) {
@@ -578,7 +623,7 @@
if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
int myWidthSpec = MeasureSpec.makeMeasureSpec(
- widthSize - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
+ getPageWidthSize(widthSize), MeasureSpec.EXACTLY);
int myHeightSpec = MeasureSpec.makeMeasureSpec(
heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
@@ -670,11 +715,10 @@
final int primaryDimension = bounds.primaryDimension;
final int childPrimaryEnd = bounds.childPrimaryEnd;
- // In case the pages are of different width, align the page to left or right edge
- // based on the orientation.
- final int pageScroll = mIsRtl
- ? (childStart - scrollOffsetStart)
- : Math.max(0, childPrimaryEnd - scrollOffsetEnd);
+ // In case the pages are of different width, align the page to left edge for non-RTL
+ // or right edge for RTL.
+ final int pageScroll =
+ mIsRtl ? childPrimaryEnd - scrollOffsetEnd : childStart - scrollOffsetStart;
if (outPageScrolls[i] != pageScroll) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
@@ -682,6 +726,19 @@
childStart += primaryDimension + mPageSpacing + getChildGap();
}
}
+
+ int panelCount = getPanelCount();
+ if (panelCount > 1) {
+ for (int i = 0; i < childCount; i++) {
+ // In case we have multiple panels, always use left panel's page scroll for all
+ // panels on the screen.
+ int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
+ if (outPageScrolls[i] != adjustedScroll) {
+ outPageScrolls[i] = adjustedScroll;
+ pageScrollChanged = true;
+ }
+ }
+ }
return pageScrollChanged;
}
@@ -794,14 +851,16 @@
}
if (direction == View.FOCUS_LEFT) {
if (getCurrentPage() > 0) {
- snapToPage(getCurrentPage() - 1);
- getChildAt(getCurrentPage() - 1).requestFocus(direction);
+ int nextPage = validateNewPage(getCurrentPage() - 1);
+ snapToPage(nextPage);
+ getChildAt(nextPage).requestFocus(direction);
return true;
}
} else if (direction == View.FOCUS_RIGHT) {
if (getCurrentPage() < getPageCount() - 1) {
- snapToPage(getCurrentPage() + 1);
- getChildAt(getCurrentPage() + 1).requestFocus(direction);
+ int nextPage = validateNewPage(getCurrentPage() + 1);
+ snapToPage(nextPage);
+ getChildAt(nextPage).requestFocus(direction);
return true;
}
}
@@ -820,11 +879,13 @@
}
if (direction == View.FOCUS_LEFT) {
if (mCurrentPage > 0) {
- getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+ int nextPage = validateNewPage(mCurrentPage - 1);
+ getPageAt(nextPage).addFocusables(views, direction, focusableMode);
}
- } else if (direction == View.FOCUS_RIGHT){
+ } else if (direction == View.FOCUS_RIGHT) {
if (mCurrentPage < getPageCount() - 1) {
- getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+ int nextPage = validateNewPage(mCurrentPage + 1);
+ getPageAt(nextPage).addFocusables(views, direction, focusableMode);
}
}
}
@@ -1004,10 +1065,7 @@
// Try canceling the long press. It could also have been scheduled
// by a distant descendant, so use the mAllowLongPress flag to block
// everything
- final View currentPage = getPageAt(mCurrentPage);
- if (currentPage != null) {
- currentPage.cancelLongPress();
- }
+ getVisiblePages().forEach(View::cancelLongPress);
}
protected float getScrollProgress(int screenCenter, View v, int page) {
@@ -1255,12 +1313,14 @@
if (((isSignificantMove && !isDeltaLeft && !isFling) ||
(isFling && !isVelocityLeft)) && mCurrentPage > 0) {
- finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+ finalPage = returnToOriginalPage
+ ? mCurrentPage : mCurrentPage - getPanelCount();
snapToPageWithVelocity(finalPage, velocity);
} else if (((isSignificantMove && isDeltaLeft && !isFling) ||
(isFling && isVelocityLeft)) &&
mCurrentPage < getChildCount() - 1) {
- finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+ finalPage = returnToOriginalPage
+ ? mCurrentPage : mCurrentPage + getPanelCount();
snapToPageWithVelocity(finalPage, velocity);
} else {
snapToDestination();
@@ -1449,17 +1509,16 @@
return getDestinationPage(mOrientationHandler.getPrimaryScroll(this));
}
- protected int getDestinationPage(int scaledScroll) {
- return getPageNearestToCenterOfScreen(scaledScroll);
+ protected int getDestinationPage(int primaryScroll) {
+ return getPageNearestToCenterOfScreen(primaryScroll);
}
public int getPageNearestToCenterOfScreen() {
return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this));
}
- private int getPageNearestToCenterOfScreen(int scaledScroll) {
- int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
- int screenCenter = scaledScroll + (pageOrientationSize / 2);
+ private int getPageNearestToCenterOfScreen(int primaryScroll) {
+ int screenCenter = getScreenCenter(primaryScroll);
int minDistanceFromScreenCenter = Integer.MAX_VALUE;
int minDistanceFromScreenCenterIndex = -1;
final int childCount = getChildCount();
@@ -1475,18 +1534,26 @@
}
private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) {
- int childSize = getChildVisibleSize(childIndex);
+ int childSize = Math.round(getChildVisibleSize(childIndex));
int halfChildSize = (childSize / 2);
int childCenter = getChildOffset(childIndex) + halfChildSize;
return childCenter - screenCenter;
}
protected int getDisplacementFromScreenCenter(int childIndex) {
- int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
- int screenCenter = mOrientationHandler.getPrimaryScroll(this) + (pageOrientationSize / 2);
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+ int screenCenter = getScreenCenter(primaryScroll);
return getDisplacementFromScreenCenter(childIndex, screenCenter);
}
+ private int getScreenCenter(int primaryScroll) {
+ float primaryScale = mOrientationHandler.getPrimaryScale(this);
+ float primaryPivot = mOrientationHandler.getPrimaryValue(getPivotX(), getPivotY());
+ int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
+ return Math.round(primaryScroll + (pageOrientationSize / 2f - primaryPivot) / primaryScale
+ + primaryPivot);
+ }
+
protected void snapToDestination() {
snapToPage(getDestinationPage(), getPageSnapDuration());
}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 8bc5ad0..858b72e 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -44,7 +44,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.net.URISyntaxException;
@@ -109,15 +108,12 @@
mCurrentAccessibilityAction = action;
if (action == UNINSTALL) {
- mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
setDrawable(R.drawable.ic_uninstall_shadow);
updateText(R.string.uninstall_drop_target_label);
} else if (action == DISMISS_PREDICTION) {
- mHoverColor = Themes.getColorAccent(getContext());
setDrawable(R.drawable.ic_block_shadow);
updateText(R.string.dismiss_prediction_label);
} else if (action == RECONFIGURE) {
- mHoverColor = Themes.getColorAccent(getContext());
setDrawable(R.drawable.ic_setup_shadow);
updateText(R.string.gadget_setup_text);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c440303..8825710 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -69,6 +69,7 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
@@ -690,6 +691,47 @@
};
}
+ /**
+ * Compares the ratio of two quantities and returns whether that ratio is greater than the
+ * provided bound. Order of quantities does not matter. Bound should be a decimal representation
+ * of a percentage.
+ */
+ public static boolean isRelativePercentDifferenceGreaterThan(float first, float second,
+ float bound) {
+ return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound;
+ }
+
+ /**
+ * Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent
+ * sizes represent the "space" that will rotate carrying inOutBounds along with it to determine
+ * the final bounds.
+ */
+ public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
+ int delta) {
+ int rdelta = ((delta % 4) + 4) % 4;
+ int origLeft = inOutBounds.left;
+ switch (rdelta) {
+ case 0:
+ return;
+ case 1:
+ inOutBounds.left = inOutBounds.top;
+ inOutBounds.top = parentWidth - inOutBounds.right;
+ inOutBounds.right = inOutBounds.bottom;
+ inOutBounds.bottom = parentWidth - origLeft;
+ return;
+ case 2:
+ inOutBounds.left = parentWidth - inOutBounds.right;
+ inOutBounds.right = parentWidth - origLeft;
+ return;
+ case 3:
+ inOutBounds.left = parentHeight - inOutBounds.bottom;
+ inOutBounds.bottom = inOutBounds.right;
+ inOutBounds.right = parentHeight - inOutBounds.top;
+ inOutBounds.top = origLeft;
+ return;
+ }
+ }
+
private static class FixedSizeEmptyDrawable extends ColorDrawable {
private final int mSize;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 446c873..f43452c 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -37,6 +37,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
@@ -540,20 +541,17 @@
c.setBitmap(preview);
c.drawColor(0, PorterDuff.Mode.CLEAR);
}
- RectF boxRect = drawBoxWithShadow(c, size, size);
+
+ drawBoxWithShadow(c, size, size);
LauncherIcons li = LauncherIcons.obtain(mContext);
- Bitmap icon = li.createBadgedIconBitmap(
+ Drawable icon = li.createBadgedIconBitmap(
mutateOnMainThread(info.getFullResIcon(mIconCache)),
- Process.myUserHandle(), 0).icon;
+ Process.myUserHandle(), 0).newIcon(launcher);
li.recycle();
- Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
-
- boxRect.set(0, 0, iconSize, iconSize);
- boxRect.offset(padding, padding);
- c.drawBitmap(icon, src, boxRect,
- new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
+ icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
+ icon.draw(c);
c.setBitmap(null);
return preview;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a089517..981cf89 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -69,6 +69,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -82,6 +83,7 @@
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.graphics.WorkspaceDragScrim;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
@@ -110,6 +112,7 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import java.util.ArrayList;
@@ -312,7 +315,9 @@
// Increase our bottom insets so we don't overlap with the taskbar.
mInsets.bottom += grid.nonOverlappingTaskbarInset;
- if (mWorkspaceFadeInAdjacentScreens) {
+ if (isTwoPanelEnabled()) {
+ setPageSpacing(0); // we have two pages and we don't want any spacing
+ } else if (mWorkspaceFadeInAdjacentScreens) {
// In landscape mode the page spacing is set to the default.
setPageSpacing(grid.edgeMarginPx);
} else {
@@ -324,12 +329,30 @@
setPageSpacing(Math.max(maxInsets, maxPadding));
}
-
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
+ int twoPanelLandscapeSidePadding = paddingLeftRight * 2;
+ int twoPanelPortraitSidePadding = paddingLeftRight / 2;
+
+ int panelCount = getPanelCount();
for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
- mWorkspaceScreens.valueAt(i)
- .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+ int paddingLeft = paddingLeftRight;
+ int paddingRight = paddingLeftRight;
+ if (panelCount > 1) {
+ if (i % panelCount == 0) { // left side panel
+ paddingLeft = grid.isLandscape ? twoPanelLandscapeSidePadding
+ : twoPanelPortraitSidePadding;
+ paddingRight = 0;
+ } else if (i % panelCount == panelCount - 1) { // right side panel
+ paddingLeft = 0;
+ paddingRight = grid.isLandscape ? twoPanelLandscapeSidePadding
+ : twoPanelPortraitSidePadding;
+ } else { // middle panel
+ paddingLeft = 0;
+ paddingRight = 0;
+ }
+ }
+ mWorkspaceScreens.valueAt(i).setPadding(paddingLeft, 0, paddingRight, paddingBottom);
}
}
@@ -398,15 +421,6 @@
layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
}
- if (mOutlineProvider != null) {
- if (dragObject.dragView != null) {
- Bitmap preview = dragObject.dragView.getPreviewBitmap();
-
- // The outline is used to visualize where the item will land if dropped
- mOutlineProvider.generateDragOutline(preview);
- }
- }
-
updateChildrenLayersEnabled();
// Do not add a new page if it is a accessible drag which was not started by the workspace.
@@ -445,6 +459,15 @@
.log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
}
+ private boolean isTwoPanelEnabled() {
+ return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get();
+ }
+
+ @Override
+ protected int getPanelCount() {
+ return isTwoPanelEnabled() ? 2 : super.getPanelCount();
+ }
+
public void deferRemoveExtraEmptyScreen() {
mDeferRemoveExtraEmptyScreen = true;
}
@@ -832,7 +855,7 @@
private boolean shouldConsumeTouch(View v) {
return !workspaceIconsCanBeDragged()
- || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
+ || (!workspaceInModalState() && !isVisible(v));
}
public boolean isSwitchingState() {
@@ -1433,7 +1456,7 @@
// TAPL can work only if UIDevice is set up as setCompressedLayoutHeirarchy(false).
// Hiding workspace from the tests when it's
// IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS.
- return null;
+ return AccessibilityNodeInfo.obtain();
}
return super.createAccessibilityNodeInfo();
}
@@ -1506,10 +1529,10 @@
draggableView = (DraggableView) child;
}
- // The drag bitmap follows the touch point around on the screen
- final Bitmap b = previewProvider.createDragBitmap();
+ // The draggable drawable follows the touch point around on the screen
+ final Drawable drawable = previewProvider.createDrawable();
int halfPadding = previewProvider.previewPadding / 2;
- float scale = previewProvider.getScaleAndPosition(b, mTempXY);
+ float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
@@ -1535,9 +1558,21 @@
}
}
- DragView dv = mDragController.startDrag(b, draggableView, dragLayerX, dragLayerY, source,
- dragObject, dragVisualizeOffset, dragRect, scale * iconScale,
- scale, dragOptions);
+ if (drawable instanceof AppWidgetHostViewDrawable) {
+ mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
+ }
+ DragView dv = mDragController.startDrag(
+ drawable,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ dragOptions);
dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
return dv;
}
@@ -1965,16 +2000,26 @@
}
/**
- * Computes the area relative to dragLayer which is used to display a page.
+ * Computes and returns the area relative to dragLayer which is used to display a page.
+ * In case we have multiple pages displayed at the same time, we return the union of the areas.
*/
- public void getPageAreaRelativeToDragLayer(Rect outArea) {
- CellLayout child = (CellLayout) getChildAt(getNextPage());
- if (child == null) {
- return;
+ public Rect getPageAreaRelativeToDragLayer() {
+ Rect area = new Rect();
+ int nextPage = getNextPage();
+ int panelCount = getPanelCount();
+ for (int page = nextPage; page < nextPage + panelCount; page++) {
+ CellLayout child = (CellLayout) getChildAt(page);
+ if (child == null) {
+ break;
+ }
+
+ ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
+ Rect tmpRect = new Rect();
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, tmpRect);
+ area.union(tmpRect);
}
- ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
+ return area;
}
@Override
@@ -2259,19 +2304,27 @@
int nextPage = getNextPage();
if (layout == null && !isPageInTransition()) {
- // Check if the item is dragged over left page
+ // Check if the item is dragged over currentPage - 1 page
mTempTouchCoordinates[0] = Math.min(centerX, d.x);
mTempTouchCoordinates[1] = d.y;
layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), mTempTouchCoordinates);
}
if (layout == null && !isPageInTransition()) {
- // Check if the item is dragged over right page
+ // Check if the item is dragged over currentPage + 1 page
mTempTouchCoordinates[0] = Math.max(centerX, d.x);
mTempTouchCoordinates[1] = d.y;
layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), mTempTouchCoordinates);
}
+ // If two panel is enabled, users can also drag items to currentPage + 2
+ if (isTwoPanelEnabled() && layout == null && !isPageInTransition()) {
+ // Check if the item is dragged over currentPage + 2 page
+ mTempTouchCoordinates[0] = Math.max(centerX, d.x);
+ mTempTouchCoordinates[1] = d.y;
+ layout = verifyInsidePage(nextPage + (mIsRtl ? -2 : 2), mTempTouchCoordinates);
+ }
+
// Always pick the current page.
if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
layout = (CellLayout) getChildAt(nextPage);
@@ -2585,7 +2638,11 @@
}
- public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
+ private Drawable createWidgetDrawable(ItemInfo widgetInfo, View layout) {
+ if (layout instanceof LauncherAppWidgetHostView) {
+ return new AppWidgetHostViewDrawable((LauncherAppWidgetHostView) layout);
+ }
+
int[] unScaledSize = estimateItemSize(widgetInfo);
int visibility = layout.getVisibility();
layout.setVisibility(VISIBLE);
@@ -2597,7 +2654,7 @@
Bitmap b = BitmapRenderer.createHardwareBitmap(
unScaledSize[0], unScaledSize[1], layout::draw);
layout.setVisibility(visibility);
- return b;
+ return new FastBitmapDrawable(b);
}
private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
@@ -2667,8 +2724,8 @@
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
- Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
- dragView.setCrossFadeBitmap(crossFadeBitmap);
+ Drawable crossFadeDrawable = createWidgetDrawable(info, finalView);
+ dragView.setCrossFadeDrawable(crossFadeDrawable);
dragView.crossFade((int) (duration * 0.8f));
} else if (isWidget && external) {
scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
@@ -2921,8 +2978,10 @@
* @param user The user of the app to match.
*/
public View getFirstMatchForAppClose(String packageName, UserHandle user) {
- final int curPage = getCurrentPage();
- final CellLayout currentPage = (CellLayout) getPageAt(curPage);
+ List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
+ cellLayouts.add(getHotseat());
+ getVisiblePages().forEach(page -> cellLayouts.add((CellLayout) page));
+
final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
@@ -2943,13 +3002,11 @@
// Order: App icons, app in folder. Items in hotseat get returned first.
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
- return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
- packageAndUserAndApp, packageAndUserAndAppInFolder);
+ return getFirstMatch(cellLayouts, packageAndUserAndApp, packageAndUserAndAppInFolder);
} else {
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
// FolderAdaptiveIcon as the background.
- return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
- packageAndUserAndApp);
+ return getFirstMatch(cellLayouts, packageAndUserAndApp);
}
}
@@ -2983,7 +3040,7 @@
* @param operators List of operators, in order starting from best matching operator.
* @return
*/
- View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
+ View getFirstMatch(Iterable<CellLayout> cellLayouts, final ItemOperator... operators) {
// This array is filled with the first match for each operator.
final View[] matches = new View[operators.length];
// For efficiency, the outer loop should be CellLayout.
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index d6d2f73..e0a4d4a 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -34,7 +34,6 @@
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
@@ -47,7 +46,6 @@
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -55,7 +53,6 @@
import com.android.launcher3.graphics.WorkspaceDragScrim;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource;
-import com.android.launcher3.util.MultiValueAlpha;
import com.android.systemui.plugins.ResourceProvider;
/**
@@ -97,7 +94,6 @@
ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
mLauncher);
- ScaleAndTranslation qsbScaleAndTranslation = state.getQsbScaleAndTranslation(mLauncher);
mNewScale = scaleAndTranslation.scale;
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
@@ -109,56 +105,39 @@
int elements = state.getVisibleElements(mLauncher);
Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
- boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
Hotseat hotseat = mWorkspace.getHotseat();
- // Since we set the pivot relative to mWorkspace, we need to scale a sibling of Workspace.
- AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
- View qsbView = qsbScaleView.getSearchView();
- if (playAtomicComponent) {
- Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
- LauncherState fromState = mLauncher.getStateManager().getState();
- boolean shouldSpring = propertySetter instanceof PendingAnimation
- && fromState == HINT_STATE && state == NORMAL;
- if (shouldSpring) {
- ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
- mWorkspace, mNewScale));
- } else {
- propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
- }
+ Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
+ LauncherState fromState = mLauncher.getStateManager().getState();
- setPivotToScaleWithWorkspace(hotseat);
- setPivotToScaleWithWorkspace(qsbScaleView);
- float hotseatScale = hotseatScaleAndTranslation.scale;
- if (shouldSpring) {
- PendingAnimation pa = (PendingAnimation) propertySetter;
- pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
- pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
- qsbScaleAndTranslation.scale));
- } else {
- Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
- scaleInterpolator);
- propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
- hotseatScaleInterpolator);
- propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
- hotseatScaleInterpolator);
- }
-
- float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
- propertySetter.setFloat(hotseat.getStateAlpha(), MultiValueAlpha.VALUE,
- hotseatIconsAlpha, config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
- float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
- propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
- workspacePageIndicatorAlpha, fadeInterpolator);
+ boolean shouldSpring = propertySetter instanceof PendingAnimation
+ && fromState == HINT_STATE && state == NORMAL;
+ if (shouldSpring) {
+ ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
+ mWorkspace, mNewScale));
+ } else {
+ propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
}
- if (config.onlyPlayAtomicComponent()) {
- // Only the alpha and scale, handled above, are included in the atomic animation.
- return;
+ setPivotToScaleWithWorkspace(hotseat);
+ float hotseatScale = hotseatScaleAndTranslation.scale;
+ if (shouldSpring) {
+ PendingAnimation pa = (PendingAnimation) propertySetter;
+ pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+ } else {
+ Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
+ scaleInterpolator);
+ propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+ hotseatScaleInterpolator);
}
- Interpolator translationInterpolator = !playAtomicComponent
- ? LINEAR
- : config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
+ float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
+ propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
+ float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
+ propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+ workspacePageIndicatorAlpha, fadeInterpolator);
+
+ Interpolator translationInterpolator =
+ config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
scaleAndTranslation.translationX, translationInterpolator);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
@@ -170,8 +149,6 @@
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
propertySetter.setFloat(mWorkspace.getPageIndicator(), VIEW_TRANSLATE_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
- propertySetter.setFloat(qsbView, VIEW_TRANSLATE_Y,
- qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator);
setScrim(propertySetter, state);
}
@@ -210,17 +187,12 @@
int drawableAlpha = state.hasFlag(FLAG_WORKSPACE_HAS_BACKGROUNDS)
? Math.round(pageAlpha * 255) : 0;
- if (!config.onlyPlayAtomicComponent()) {
- // Don't update the scrim during the atomic animation.
- propertySetter.setInt(cl.getScrimBackground(),
- DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
- }
- if (config.playAtomicOverviewScaleComponent()) {
- Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
- pageAlphaProvider.interpolator);
- propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
- pageAlpha, fadeInterpolator);
- }
+ propertySetter.setInt(cl.getScrimBackground(),
+ DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
+ Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
+ pageAlphaProvider.interpolator);
+ propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
+ pageAlpha, fadeInterpolator);
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index fdc69ec..225e1c1 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
@@ -63,7 +62,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiValueAlpha;
@@ -79,7 +77,7 @@
public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
- private static final float FLING_VELOCITY_MULTIPLIER = 135f;
+ private static final float FLING_VELOCITY_MULTIPLIER = 1000 * .8f;
// Starts the springs after at least 55% of the animation has passed.
private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
private static final int ALPHA_CHANNEL_COUNT = 2;
@@ -142,12 +140,7 @@
mAllAppsStore.addUpdateListener(this::onAppsUpdated);
- addSpringView(R.id.all_apps_header);
- addSpringView(R.id.apps_list_view);
- addSpringView(R.id.all_apps_tabs_view_pager);
-
mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
-
}
/**
@@ -171,17 +164,10 @@
return mWorkModeSwitch;
}
-
- @Override
- protected void setDampedScrollShift(float shift) {
- // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
- float maxShift = getSearchView().getHeight() / 2f;
- super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift));
- }
-
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
+ holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns);
if (holder.recyclerView != null) {
// Remove all views and clear the pool, while keeping the data same. After this
// call, all the viewHolders will be recreated.
@@ -343,7 +329,7 @@
mSearchContainer = findViewById(R.id.search_container_all_apps);
mSearchUiManager = (SearchUiManager) mSearchContainer;
- mSearchUiManager.initialize(this);
+ mSearchUiManager.initializeSearch(this);
}
public SearchUiManager getSearchUiManager() {
@@ -409,12 +395,6 @@
}
}
- @Override
- public int getCanvasClipTopForOverscroll() {
- // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
- return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
- }
-
private void rebindAdapters(boolean showTabs) {
rebindAdapters(showTabs, false /* force */);
}
@@ -564,37 +544,9 @@
/**
* Handles selection on focused view and returns success
*/
- public boolean selectFocusedView(View v) {
- ItemInfo headerItem = getHighlightedItemFromHeader();
- if (headerItem != null) {
- return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
- }
- AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
- if (focusedItem != null) {
- View focusedView = getActiveRecyclerView().getLayoutManager()
- .findViewByPosition(focusedItem.position);
- if (focusedView != null && mSearchAdapterProvider.onAdapterItemSelected(focusedItem,
- focusedView)) {
- return true;
- }
- }
- if (focusedItem != null && focusedItem.appInfo != null) {
- ItemInfo itemInfo = focusedItem.appInfo;
- return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo);
- }
- return false;
- }
-
- /**
- * Returns the ItemInfo of a focused view inside {@link FloatingHeaderView}
- */
- public ItemInfo getHighlightedItemFromHeader() {
- View view = getFloatingHeaderView().getFocusedChild();
- if (view != null && view.getTag() instanceof ItemInfo) {
- return ((ItemInfo) view.getTag());
- }
-
- return null;
+ public boolean launchHighlightedItem() {
+ if (mSearchAdapterProvider == null) return false;
+ return mSearchAdapterProvider.launchHighlightedItem();
}
public SearchAdapterProvider getSearchAdapterProvider() {
@@ -613,10 +565,6 @@
int padding = mHeader.getMaxTranslation();
for (int i = 0; i < mAH.length; i++) {
mAH[i].padding.top = padding;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mUsingTabs) {
- //add extra space between tabs and recycler view
- mAH[i].padding.top += mLauncher.getDeviceProfile().edgeMarginPx;
- }
mAH[i].applyPadding();
}
}
@@ -673,11 +621,8 @@
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (shouldSpring
&& valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
- int searchViewId = getSearchView().getId();
- addSpringView(searchViewId);
- finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
- (anim, canceled, value, velocity) -> removeSpringView(searchViewId));
-
+ absorbSwipeUpVelocity(Math.abs(
+ Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
shouldSpring = false;
}
}
@@ -733,8 +678,7 @@
applyPadding();
setupOverlay();
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- recyclerView.addItemDecoration(new AllAppsSectionDecorator(
- AllAppsContainerView.this));
+ recyclerView.addItemDecoration(mSearchAdapterProvider.getDecorator());
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index bb175ea..5b4c4c5 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -42,7 +42,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -108,7 +107,7 @@
// The index of this app not including sections
public int appIndex = -1;
// Search section associated to result
- public SectionDecorationInfo sectionDecorationInfo = null;
+ public DecorationInfo decorationInfo = null;
/**
* Factory method for AppIcon AdapterItem
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index 14e3b51..3cc9ce6 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -23,8 +23,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
-import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
/**
@@ -43,10 +41,6 @@
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
- : context.getResources().getDimensionPixelOffset(
- R.dimen.all_apps_header_top_padding);
- setPadding(0, topPadding, 0, 0);
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index f307a53..66575eb 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -189,6 +189,7 @@
case SCROLL_STATE_DRAGGING:
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
+ requestFocus();
getWindowInsetsController().hide(WindowInsets.Type.ime());
break;
case SCROLL_STATE_IDLE:
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
deleted file mode 100644
index 269e390..0000000
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.ColorUtils;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
-import com.android.launcher3.util.Themes;
-
-import java.util.List;
-
-/**
- * ItemDecoration class that groups items in {@link AllAppsRecyclerView}
- */
-public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
-
- private final AllAppsContainerView mAppsView;
-
- AllAppsSectionDecorator(AllAppsContainerView appsContainerView) {
- mAppsView = appsContainerView;
- }
-
- @Override
- public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
- List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
- for (int i = 0; i < parent.getChildCount(); i++) {
- View view = parent.getChildAt(i);
- int position = parent.getChildAdapterPosition(view);
- AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
- if (adapterItem.sectionDecorationInfo != null) {
- SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
- SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
- if (decorationHandler != null) {
- if (sectionInfo.isFocusedView()) {
- decorationHandler.onFocusDraw(c, view);
- } else {
- decorationHandler.onGroupDraw(c, view);
- }
- }
- }
- }
- }
-
- // Fallback logic in case non of the SearchTarget is labeled as focused item.
- private void drawDecoration(@NonNull Canvas c,
- @NonNull SectionDecorationHandler decorationHandler,
- @NonNull RecyclerView parent) {
- if (decorationHandler.mIsFullWidth) {
- decorationHandler.mBounds.left = parent.getPaddingLeft();
- decorationHandler.mBounds.right = parent.getWidth() - parent.getPaddingRight();
- }
- if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
- && mAppsView.getApps().getFocusedChild() != null) {
- int index = mAppsView.getApps().getFocusedChildIndex();
- AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
- mAppsView.getActiveRecyclerView().getLayoutManager();
- if (layoutManager.findFirstVisibleItemPosition() <= index
- && index < parent.getChildCount()) {
- RecyclerView.ViewHolder vh = parent.findViewHolderForAdapterPosition(index);
- if (vh != null) decorationHandler.onFocusDraw(c, vh.itemView);
- }
- }
- decorationHandler.reset();
- }
-
- /**
- * Handles grouping and drawing of items in the same all apps sections.
- */
- public static class SectionDecorationHandler {
- protected RectF mBounds = new RectF();
- private final boolean mIsFullWidth;
- private final float mRadius;
-
- protected final int mFocusColor; // main focused item color
- protected final int mFillcolor; // grouping color
-
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final boolean mIsTopRound;
- private final boolean mIsBottomRound;
- private float [] mCorners;
- private float mFillSpacing;
-
- public SectionDecorationHandler(Context context, boolean isFullWidth, int fillAlpha,
- boolean isTopRound, boolean isBottomRound) {
-
- mIsFullWidth = isFullWidth;
- int endScrim = Themes.getColorBackground(context);
- mFillcolor = ColorUtils.setAlphaComponent(endScrim, fillAlpha);
- mFocusColor = endScrim;
-
- mIsTopRound = isTopRound;
- mIsBottomRound = isBottomRound;
-
- mRadius = context.getResources().getDimensionPixelSize(
- R.dimen.search_decoration_corner_radius);
- mFillSpacing = context.getResources().getDimensionPixelSize(
- R.dimen.search_decoration_padding);
- mCorners = new float[]{
- mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top left radius in px
- mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top right radius in px
- mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0, // Bottom right
- mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0 // Bottom left
- };
-
- }
-
- /**
- * Draw bounds onto canvas.
- */
- public void onGroupDraw(Canvas canvas, View view) {
- if (view == null) return;
-
- mPaint.setColor(mFillcolor);
- mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
- onDraw(canvas);
- }
-
- /**
- * Draw the bound of the view to the canvas.
- */
- public void onFocusDraw(Canvas canvas, @Nullable View view) {
- if (view == null) {
- return;
- }
- mPaint.setColor(mFocusColor);
- mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
- onDraw(canvas);
- }
-
-
- private void onDraw(Canvas canvas) {
- final Path path = new Path();
- RectF finalBounds = new RectF(mBounds.left + mFillSpacing,
- mBounds.top + mFillSpacing,
- mBounds.right - mFillSpacing,
- mBounds.bottom - mFillSpacing);
- path.addRoundRect(finalBounds, mCorners, Path.Direction.CW);
- canvas.drawPath(path, mPaint);
- }
-
- /**
- * Reset view bounds to empty.
- */
- public void reset() {
- mBounds.setEmpty();
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 1e6f829..8c5b0fe 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -16,16 +16,11 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
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.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
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_ALL_APPS_HEADER_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;
@@ -160,19 +155,12 @@
StateAnimationConfig config, PendingAnimation builder) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
- if (!config.onlyPlayAtomicComponent()) {
- setAlphas(toState, config, builder);
- }
+ setAlphas(toState, config, builder);
// Fail fast
onProgressAnimationEnd();
return;
}
- if (config.onlyPlayAtomicComponent()) {
- // There is no atomic component for the all apps transition, so just return early.
- return;
- }
-
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
@@ -194,25 +182,10 @@
*/
public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
int visibleElements = state.getVisibleElements(mLauncher);
- boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
- boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
-
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
- Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
-
-
- setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
- mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
- hasAllAppsContent, setter, headerFade, allAppsFade);
-
- mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
-
- // Set visibility of the container at the very beginning or end of the transition.
- setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
- hasAnyVisibleItem ? INSTANT : FINAL_FRAME);
+ setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
}
public AnimatorListenerAdapter getProgressAnimatorListener() {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index fefd97a..6957850 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -20,7 +20,6 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import com.android.launcher3.allapps.search.SectionDecorationInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -288,11 +287,6 @@
mFastScrollerSections.clear();
mAdapterItems.clear();
- SectionDecorationInfo appSection = new SectionDecorationInfo();
- appSection.setDecorationHandler(
- new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true,
- 0, false, false));
-
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
@@ -313,9 +307,7 @@
if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
}
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- appItem.sectionDecorationInfo = appSection;
- }
+
mAdapterItems.add(appItem);
}
} else {
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/allapps/DecorationInfo.java
similarity index 69%
rename from src/com/android/launcher3/util/SafeCloseable.java
rename to src/com/android/launcher3/allapps/DecorationInfo.java
index ba8ee04..50b250c 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/allapps/DecorationInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,14 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.launcher3.allapps;
-package com.android.launcher3.util;
-
-/**
- * Extension of closeable which does not throw an exception
- */
-public interface SafeCloseable extends AutoCloseable {
-
- @Override
- void close();
+public class DecorationInfo {
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 31c6cc7..9bf6043 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -17,10 +17,8 @@
import android.graphics.Rect;
import android.view.View;
-import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
/**
* A abstract representation of a row in all-apps view
@@ -47,9 +45,6 @@
*/
boolean hasVisibleContent();
- void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
- PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade);
-
/**
* Scrolls the content vertically.
*/
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 9056e8a..733d867 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
@@ -26,7 +24,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@@ -37,7 +34,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
-import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.systemui.plugins.AllAppsRow;
@@ -88,7 +84,6 @@
private int mSnappedScrolledY;
private int mTranslationY;
- private boolean mAllowTouchForwarding;
private boolean mForwardToRecyclerView;
protected boolean mTabsHidden;
@@ -111,8 +106,8 @@
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
- context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
+ mHeaderTopPadding = context.getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
}
@Override
@@ -130,7 +125,6 @@
}
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
- setPadding(0, mHeaderTopPadding, 0, 0);
mAllRows = mFixedRows;
}
@@ -248,9 +242,7 @@
public int getMaxTranslation() {
if (mMaxTranslation == 0 && mTabsHidden) {
- int paddingOffset = getResources().getDimensionPixelSize(
- R.dimen.all_apps_search_bar_bottom_padding);
- return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset;
+ return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
} else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop();
} else {
@@ -350,10 +342,6 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (!mAllowTouchForwarding) {
- mForwardToRecyclerView = false;
- return super.onInterceptTouchEvent(ev);
- }
calcOffset(mTempOffset);
ev.offsetLocation(mTempOffset.x, mTempOffset.y);
mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev);
@@ -382,20 +370,6 @@
p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
}
- public void setContentVisibility(boolean hasHeader, boolean hasAllAppsContent,
- PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
- for (FloatingHeaderRow row : mAllRows) {
- row.setContentVisibility(hasHeader, hasAllAppsContent, setter, headerFade, allAppsFade);
- }
-
- allowTouchForwarding(hasAllAppsContent);
- setter.setFloat(mTabLayout, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
- }
-
- protected void allowTouchForwarding(boolean allow) {
- mAllowTouchForwarding = allow;
- }
-
public boolean hasVisibleContent() {
for (FloatingHeaderRow row : mAllRows) {
if (row.hasVisibleContent()) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index cf7142c..5b5fbb7 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -18,14 +18,10 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
import android.graphics.Rect;
import android.view.View;
-import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
import com.android.systemui.plugins.AllAppsRow;
/**
@@ -65,13 +61,6 @@
}
@Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
- PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
- // Don't use setViewAlpha as we want to control the visibility ourselves.
- setter.setFloat(mView, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
- }
-
- @Override
public void setVerticalScroll(int scroll, boolean isScrolledOut) {
mView.setVisibility(isScrolledOut ? INVISIBLE : VISIBLE);
if (!isScrolledOut) {
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 0d42950..941d3af 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,16 +15,12 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
-
import android.graphics.Rect;
import android.view.KeyEvent;
-import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.anim.PropertySetter;
/**
* Interface for controlling the Apps search UI.
@@ -34,7 +30,7 @@
/**
* Initializes the search manager.
*/
- void initialize(AllAppsContainerView containerView);
+ void initializeSearch(AllAppsContainerView containerView);
/**
* Notifies the search manager to close any active search session.
@@ -45,7 +41,7 @@
* Called before dispatching a key event, in case the search manager wants to initialize
* some UI beforehand.
*/
- void preDispatchKeyEvent(KeyEvent keyEvent);
+ default void preDispatchKeyEvent(KeyEvent keyEvent) { };
/**
* Returns the vertical shift for the all-apps view, so that it aligns with the hotseat.
@@ -53,25 +49,6 @@
float getScrollRangeDelta(Rect insets);
/**
- * Called as part of state transition to update the content UI
- */
- void setContentVisibility(int visibleElements, PropertySetter setter,
- Interpolator interpolator);
-
- /**
- * Called when activity is destroyed. Used to close search system services
- */
- default void destroy() {
- }
-
- /**
- * Returns true if the QSB should be visible for the given set of visible elements
- */
- default boolean isQsbVisible(int visibleElements) {
- return (visibleElements & ALL_APPS_HEADER) != 0;
- }
-
- /**
* @return the edit text object
*/
@Nullable
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index d3c9993..79718fb 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -35,7 +35,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
-import com.android.launcher3.util.PackageManagerHelper;
/**
* An interface to a search box that AllApps can command.
@@ -105,30 +104,14 @@
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
- mLauncher.getStatsLogManager().logger()
- .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
- // selectFocusedView should return SearchTargetEvent that is passed onto onClick
- if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
- return true;
- }
- }
- }
- // Skip if it's not the right action
- if (actionId != EditorInfo.IME_ACTION_SEARCH) {
- return false;
+ if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
+ mLauncher.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
+ // selectFocusedView should return SearchTargetEvent that is passed onto onClick
+ return Launcher.getLauncher(mLauncher).getAppsView().launchHighlightedItem();
}
-
- // Skip if the query is empty
- String query = v.getText().toString();
- if (query.isEmpty()) {
- return false;
- }
- return mLauncher.startActivitySafely(v,
- PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null
- );
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 2261d51..c51bcf5 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -31,20 +31,17 @@
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
-import android.view.animation.Interpolator;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
-import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.search.SearchCallback;
@@ -134,11 +131,11 @@
}
@Override
- public void initialize(AllAppsContainerView appsView) {
+ public void initializeSearch(AllAppsContainerView appsView) {
mApps = appsView.getApps();
mAppsView = appsView;
mSearchBarController.initialize(
- new DefaultAppSearchAlgorithm(mLauncher, LauncherAppState.getInstance(mLauncher)),
+ new DefaultAppSearchAlgorithm(mLauncher),
this, mLauncher, this);
}
@@ -223,12 +220,6 @@
}
@Override
- public void setContentVisibility(int visibleElements, PropertySetter setter,
- Interpolator interpolator) {
- setter.setViewAlpha(this, isQsbVisible(visibleElements) ? 1 : 0, interpolator);
- }
-
- @Override
public ExtendedEditText getEditText() {
return this;
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
deleted file mode 100644
index 34895ed..0000000
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps.search;
-
-import android.content.Context;
-import android.os.CancellationSignal;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
-import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
-import com.android.launcher3.model.AllAppsList;
-import com.android.launcher3.model.BaseModelUpdateTask;
-import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.search.StringMatcherUtility;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * A device search section for handling app searches
- */
-public class AppsSearchPipeline implements SearchPipeline {
-
- private static final int MAX_RESULTS_COUNT = 5;
-
- private final SectionDecorationInfo mSearchSectionInfo;
- private final LauncherAppState mLauncherAppState;
-
- public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) {
- mLauncherAppState = launcherAppState;
- mSearchSectionInfo = new SectionDecorationInfo();
- mSearchSectionInfo.setDecorationHandler(
- new SectionDecorationHandler(context, true, 0, true, true));
- }
-
- @Override
- public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
- CancellationSignal cancellationSignal) {
- mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
- @Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
- callback.accept(getAdapterItems(matchingResults));
- }
- });
- }
-
- /**
- * Filters {@link AppInfo}s matching specified query
- */
- public static ArrayList<AppInfo> getTitleMatchResult(List<AppInfo> apps, String query) {
- // Do an intersection of the words in the query and each title, and filter out all the
- // apps that don't match all of the words in the query.
- final String queryTextLower = query.toLowerCase();
- final ArrayList<AppInfo> result = new ArrayList<>();
- StringMatcherUtility.StringMatcher matcher =
- StringMatcherUtility.StringMatcher.getInstance();
- for (AppInfo info : apps) {
- if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
- result.add(info);
- }
- }
- return result;
- }
-
- private ArrayList<AdapterItem> getAdapterItems(List<AppInfo> matchingApps) {
- ArrayList<AdapterItem> items = new ArrayList<>();
- for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) {
- AdapterItem appItem = AdapterItem.asApp(i, "", matchingApps.get(i), i);
- appItem.sectionDecorationInfo = mSearchSectionInfo;
- items.add(appItem);
- }
-
- return items;
- }
-}
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index a386ef8..1f854c6 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -15,25 +15,39 @@
*/
package com.android.launcher3.allapps.search;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.Context;
import android.os.Handler;
+import androidx.annotation.AnyThread;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.search.StringMatcherUtility;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* The default search implementation.
*/
public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> {
- protected final Handler mResultHandler;
- private final AppsSearchPipeline mAppsSearchPipeline;
+ private static final int MAX_RESULTS_COUNT = 5;
- public DefaultAppSearchAlgorithm(Context context, LauncherAppState launcherAppState) {
- mResultHandler = new Handler();
- mAppsSearchPipeline = new AppsSearchPipeline(context, launcherAppState);
+ private final LauncherAppState mAppState;
+ private final Handler mResultHandler;
+
+ public DefaultAppSearchAlgorithm(Context context) {
+ mAppState = LauncherAppState.getInstance(context);
+ mResultHandler = new Handler(MAIN_EXECUTOR.getLooper());
}
@Override
@@ -44,11 +58,38 @@
}
@Override
- public void doSearch(final String query,
- final SearchCallback<AdapterItem> callback) {
- mAppsSearchPipeline.query(query,
- results -> mResultHandler.post(
- () -> callback.onSearchResult(query, results)),
- null);
+ public void doSearch(String query, SearchCallback<AdapterItem> callback) {
+ mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ ArrayList<AdapterItem> result = getTitleMatchResult(apps.data, query);
+ mResultHandler.post(() -> callback.onSearchResult(query, result));
+ }
+ });
+ }
+
+ /**
+ * Filters {@link AppInfo}s matching specified query
+ */
+ @AnyThread
+ public static ArrayList<AdapterItem> getTitleMatchResult(List<AppInfo> apps, String query) {
+ // Do an intersection of the words in the query and each title, and filter out all the
+ // apps that don't match all of the words in the query.
+ final String queryTextLower = query.toLowerCase();
+ final ArrayList<AdapterItem> result = new ArrayList<>();
+ StringMatcherUtility.StringMatcher matcher =
+ StringMatcherUtility.StringMatcher.getInstance();
+
+ int resultCount = 0;
+ int total = apps.size();
+ for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
+ AppInfo info = apps.get(i);
+ if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
+ AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount);
+ result.add(appItem);
+ resultCount++;
+ }
+ }
+ return result;
}
}
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index e268f56..ef62da4 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -15,31 +15,45 @@
*/
package com.android.launcher3.allapps.search;
-import android.net.Uri;
+import android.graphics.Canvas;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.model.data.ItemInfo;
/**
* Provides views for local search results
*/
public class DefaultSearchAdapterProvider extends SearchAdapterProvider {
- public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) {
- super(launcher);
+ private final RecyclerView.ItemDecoration mDecoration;
+ private View mHighlightedView;
+
+ public DefaultSearchAdapterProvider(BaseDraggingActivity launcher,
+ AllAppsContainerView appsContainerView) {
+ super(launcher, appsContainerView);
+ mDecoration = new RecyclerView.ItemDecoration() {
+ @Override
+ public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent,
+ @NonNull RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+ }
+ };
}
@Override
public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) {
-
- }
-
- @Override
- public void onSliceStatusUpdate(Uri sliceUri) {
-
+ if (position == 0) {
+ mHighlightedView = holder.itemView;
+ }
}
@Override
@@ -54,7 +68,22 @@
}
@Override
- public boolean onAdapterItemSelected(AllAppsGridAdapter.AdapterItem adapterItem, View view) {
+ public boolean launchHighlightedItem() {
+ if (mHighlightedView instanceof BubbleTextView
+ && mHighlightedView.getTag() instanceof ItemInfo) {
+ ItemInfo itemInfo = (ItemInfo) mHighlightedView.getTag();
+ return mLauncher.startActivitySafely(mHighlightedView, itemInfo.getIntent(), itemInfo);
+ }
return false;
}
+
+ @Override
+ public View getHighlightedItem() {
+ return mHighlightedView;
+ }
+
+ @Override
+ public RecyclerView.ItemDecoration getDecorator() {
+ return mDecoration;
+ }
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 0864090..cefb8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -21,7 +21,10 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
/**
@@ -31,7 +34,7 @@
protected final BaseDraggingActivity mLauncher;
- public SearchAdapterProvider(BaseDraggingActivity launcher) {
+ public SearchAdapterProvider(BaseDraggingActivity launcher, AllAppsContainerView appsView) {
mLauncher = launcher;
}
@@ -43,7 +46,8 @@
/**
* Called from LiveSearchManager to notify slice status updates.
*/
- public abstract void onSliceStatusUpdate(Uri sliceUri);
+ public void onSliceStatusUpdate(Uri sliceUri) {
+ }
/**
* Returns whether or not viewType can be handled by searchProvider
@@ -71,9 +75,18 @@
}
/**
- * handles selection event on search adapter item. Returns false if provider can not handle
+ * Handles selection event on search adapter item. Returns false if provider can not handle
* event
*/
- public abstract boolean onAdapterItemSelected(AllAppsGridAdapter.AdapterItem adapterItem,
- View view);
+ public abstract boolean launchHighlightedItem();
+
+ /**
+ * Returns the current highlighted view
+ */
+ public abstract View getHighlightedItem();
+
+ /**
+ * Returns the item decorator.
+ */
+ public abstract RecyclerView.ItemDecoration getDecorator();
}
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
deleted file mode 100644
index 3516a41..0000000
--- a/src/com/android/launcher3/allapps/search/SearchPipeline.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps.search;
-
-import android.os.CancellationSignal;
-
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * An interface for handling search within pipeline
- */
-// Remove when System Service API is added.
-public interface SearchPipeline {
-
- /**
- * Perform query
- */
- void query(String input,
- Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
- CancellationSignal cancellationSignal);
-}
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
deleted file mode 100644
index 8e5f8cb..0000000
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps.search;
-
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.widget.RemoteViews;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A placeholder {@link AppWidgetHostView} used for managing widget search results
- */
-public class SearchWidgetInfoContainer extends AppWidgetHostView {
- private int mAppWidgetId;
- private AppWidgetProviderInfo mProviderInfo;
- private RemoteViews mViews;
- private List<AppWidgetHostView> mListeners = new ArrayList<>();
-
- public SearchWidgetInfoContainer(Context context) {
- super(context);
- }
-
- @Override
- public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
- mAppWidgetId = appWidgetId;
- mProviderInfo = info;
- for (AppWidgetHostView listener : mListeners) {
- listener.setAppWidget(mAppWidgetId, mProviderInfo);
- }
- }
-
- @Override
- public void updateAppWidget(RemoteViews remoteViews) {
- mViews = remoteViews;
- for (AppWidgetHostView listener : mListeners) {
- listener.updateAppWidget(remoteViews);
- }
- }
-
- /**
- * Create a live {@link AppWidgetHostView} from placeholder
- */
- public void attachWidget(AppWidgetHostView hv) {
- hv.setTag(getTag());
- hv.setAppWidget(mAppWidgetId, mProviderInfo);
- hv.updateAppWidget(mViews);
- mListeners.add(hv);
- }
-
- /**
- * stops AppWidgetHostView from getting updates
- */
- public void detachWidget(AppWidgetHostView hostView) {
- mListeners.remove(hostView);
- }
-
-}
diff --git a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
deleted file mode 100644
index 0b64fca..0000000
--- a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps.search;
-
-import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
-
-/**
- * Info class for a search section that is primarily used for decoration.
- */
-public class SectionDecorationInfo {
-
- public static final int QUICK_LAUNCH = 1 << 0;
- public static final int GROUPING = 1 << 1;
-
- private String mSectionId;
- private boolean mFocused;
- private SectionDecorationHandler mDecorationHandler;
-
- public boolean isFocusedView() {
- return mFocused;
- }
-
- public void setFocusedView(boolean focused) {
- mFocused = focused;
- }
-
- public SectionDecorationInfo() {
- this(null);
- }
-
- public SectionDecorationInfo(String sectionId) {
- mSectionId = sectionId;
- }
-
- public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
- mDecorationHandler = sectionDecorationHandler;
- }
-
- public SectionDecorationHandler getDecorationHandler() {
- return mDecorationHandler;
- }
-
- /**
- * Returns the section's ID
- */
- public String getSectionId() {
- return mSectionId == null ? "" : mSectionId;
- }
-}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 6b9ed09..7980138 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -77,6 +77,9 @@
public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL =
+ v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v));
+
/**
* Inversion of ZOOM_OUT, compounded with an ease-out.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 48e41d5..c7f0133 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -85,7 +85,7 @@
"ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
- "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
+ "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview");
// Keep as DeviceFlag to allow remote disable in emergency.
public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
@@ -93,7 +93,7 @@
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
- "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
+ "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
@@ -140,6 +140,9 @@
public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
"ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
+ public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag(
+ "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", false, "Enable AiAi search in the widgets picker");
+
public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
"ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
@@ -184,7 +187,7 @@
"EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
+ "Any apps occupying the first row will be removed from workspace.");
- public static final BooleanFlag ENABLE_FOUR_COLUMNS = new DeviceFlag(
+ public static final DeviceFlag ENABLE_FOUR_COLUMNS = new DeviceFlag(
"ENABLE_FOUR_COLUMNS", false, "Uses 4 columns in launcher grid."
+ "Warning: This will permanently alter your home screen items and is not reversible.");
@@ -205,9 +208,16 @@
"ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
+ "Only applicable on large screen devices.");
+ public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(
+ "ENABLE_TWO_PANEL_HOME", false,
+ "Uses two panel on home screen. Only applicable on large screen devices.");
+
public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
"ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
+ public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
+ "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -217,6 +227,12 @@
}
}
+ public static void removeFlag(DebugFlag flag) {
+ synchronized (sDebugFlags) {
+ sDebugFlags.remove(flag);
+ }
+ }
+
static List<DebugFlag> getDebugFlags() {
synchronized (sDebugFlags) {
return new ArrayList<>(sDebugFlags);
@@ -294,6 +310,15 @@
.getBoolean(key, defaultValue);
}
+ /**
+ * Resets value to default value.
+ */
+ public void reset(Context context) {
+ mCurrentValue = defaultValue;
+ context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+ .edit().putBoolean(key, defaultValue).apply();
+ }
+
@Override
protected StringBuilder appendProps(StringBuilder src) {
return super.appendProps(src).append(", mCurrentValue=").append(mCurrentValue);
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index c972cbb..b7a7366 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -37,11 +37,14 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
+import android.view.WindowManager;
+import android.widget.TextView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InvariantDeviceProfile;
@@ -127,6 +130,9 @@
if (savedInstanceState == null) {
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
}
+
+ TextView widgetAppName = findViewById(R.id.widget_appName);
+ widgetAppName.setText(getApplicationInfo().labelRes);
}
@Override
@@ -142,7 +148,7 @@
// If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
// we abort the drag.
- if (img.getBitmap() == null) {
+ if (img.getDrawable() == null) {
return false;
}
@@ -151,7 +157,7 @@
// Start home and pass the draw request params
PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
- img.getBitmap().getWidth(), img.getWidth());
+ img.getDrawable().getIntrinsicWidth(), img.getWidth());
// Start a system drag and drop. We use a transparent bitmap as preview for system drag
@@ -326,4 +332,15 @@
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
.log(command);
}
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ View view = getWindow().getDecorView();
+ WindowManager.LayoutParams layoutParams =
+ (WindowManager.LayoutParams) view.getLayoutParams();
+ layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ getWindowManager().updateViewLayout(view, layoutParams);
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
new file mode 100644
index 0000000..5cd95dc
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
@@ -0,0 +1,104 @@
+/*
+ * 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.dragndrop;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+/** A drawable which renders {@link LauncherAppWidgetHostView} to a canvas. */
+public final class AppWidgetHostViewDrawable extends Drawable {
+
+ private final LauncherAppWidgetHostView mAppWidgetHostView;
+ private Paint mPaint = new Paint();
+ private final Path mClipPath;
+
+ public AppWidgetHostViewDrawable(LauncherAppWidgetHostView appWidgetHostView) {
+ mAppWidgetHostView = appWidgetHostView;
+ Path clipPath = null;
+ if (appWidgetHostView.getClipToOutline()) {
+ Outline outline = new Outline();
+ mAppWidgetHostView.getOutlineProvider().getOutline(mAppWidgetHostView, outline);
+ Rect rect = new Rect();
+ if (outline.getRect(rect)) {
+ float radius = outline.getRadius();
+ clipPath = new Path();
+ clipPath.addRoundRect(new RectF(rect), radius, radius, Path.Direction.CCW);
+ }
+ }
+ mClipPath = clipPath;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int saveCount = canvas.saveLayer(0, 0, getIntrinsicWidth(), getIntrinsicHeight(), mPaint);
+ if (mClipPath != null) {
+ canvas.clipPath(mClipPath);
+ }
+ mAppWidgetHostView.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mAppWidgetHostView.getMeasuredWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mAppWidgetHostView.getMeasuredHeight();
+ }
+
+ @Override
+ public int getOpacity() {
+ // This is up to app widget provider. We don't know if the host view will cover anything
+ // behind the drawable.
+ return PixelFormat.UNKNOWN;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public int getAlpha() {
+ return mPaint.getAlpha();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public ColorFilter getColorFilter() {
+ return mPaint.getColorFilter();
+ }
+
+ /** Returns the {@link LauncherAppWidgetHostView}. */
+ public LauncherAppWidgetHostView getAppWidgetHostView() {
+ return mAppWidgetHostView;
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 93df599..b7a70cb 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -25,9 +25,9 @@
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -129,7 +129,7 @@
* drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded
* mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode.
*
- * @param b The bitmap to display as the drag image. It will be re-scaled to the
+ * @param drawable The drawable to be displayed in the drag view. It will be re-scaled to the
* enlarged size.
* @param originalView The source view (ie. icon, widget etc.) that is being dragged
* and which the DragView represents
@@ -140,9 +140,18 @@
* @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
* Makes dragging feel more precise, e.g. you can clip out a transparent border
*/
- public DragView startDrag(Bitmap b, DraggableView originalView, int dragLayerX, int dragLayerY,
- DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
- float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
+ public DragView startDrag(
+ Drawable drawable,
+ DraggableView originalView,
+ int dragLayerX,
+ int dragLayerY,
+ DragSource source,
+ ItemInfo dragInfo,
+ Point dragOffset,
+ Rect dragRegion,
+ float initialDragViewScale,
+ float dragViewScaleOnDrop,
+ DragOptions options) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
@@ -173,8 +182,14 @@
final Resources res = mLauncher.getResources();
final float scaleDps = mIsInPreDrag
? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
- final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
- registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
+ final DragView dragView = mDragObject.dragView = new DragView(
+ mLauncher,
+ drawable,
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 7a6b4f9..419c3f1 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -110,6 +110,8 @@
mDragController = dragController;
recreateControllers();
+ mOverviewScrim.setup();
+
mWorkspaceDragScrim = new WorkspaceDragScrim((this));
mWorkspaceDragScrim.setWorkspace(workspace);
@@ -118,8 +120,6 @@
mRootView = (LauncherRootView) getParent();
mSysUiScrim = new SysUiScrim(mRootView);
mRootView.setSysUiScrim(mSysUiScrim);
-
-
}
@Override
@@ -555,7 +555,6 @@
public void setInsets(Rect insets) {
super.setInsets(insets);
mSysUiScrim.onInsetsChanged(insets, mAllowSysuiScrims);
- mOverviewScrim.onInsetsChanged(insets);
}
public WorkspaceDragScrim getWorkspaceDragScrim() {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 86b93d0..0f26ff4 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -44,7 +44,6 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
@@ -52,6 +51,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager.StateListener;
@@ -67,9 +67,9 @@
public static final int COLOR_CHANGE_DURATION = 120;
public static final int VIEW_ZOOM_DURATION = 150;
- private boolean mDrawBitmap = true;
- private Bitmap mBitmap;
- private Bitmap mCrossFadeBitmap;
+ private boolean mShouldDraw = true;
+ private Drawable mDrawable;
+ private Drawable mCrossFadeDrawable;
@Thunk Paint mPaint;
private final int mBlurSizeOutline;
private final int mRegistrationX;
@@ -114,19 +114,21 @@
* The registration point is the point inside our view that the touch events should
* be centered upon.
* @param launcher The Launcher instance
- * @param bitmap The view that we're dragging around. We scale it up when we draw it.
+ * @param drawable The view that we're dragging around. We scale it up when we draw it.
* @param registrationX The x coordinate of the registration point.
* @param registrationY The y coordinate of the registration point.
*/
- public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
- final float initialScale, final float scaleOnDrop, final float finalScaleDps) {
+ public DragView(Launcher launcher, Drawable drawable, int registrationX,
+ int registrationY, final float initialScale, final float scaleOnDrop,
+ final float finalScaleDps) {
super(launcher);
mLauncher = launcher;
mDragLayer = launcher.getDragLayer();
mDragController = launcher.getDragController();
mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
- final float scale = (bitmap.getWidth() + finalScaleDps) / bitmap.getWidth();
+ final float scale = (drawable.getIntrinsicWidth() + finalScaleDps)
+ / drawable.getIntrinsicWidth();
// Set the initial scale to avoid any jumps
setScaleX(initialScale);
@@ -144,8 +146,9 @@
}
});
- mBitmap = bitmap;
- setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
+ mDrawable = drawable;
+ setDragRegion(new Rect(0, 0, drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight()));
// The point in our scaled bitmap that the touch events are located
mRegistrationX = registrationX;
@@ -197,8 +200,8 @@
@Override
public void run() {
Object[] outObj = new Object[1];
- int w = mBitmap.getWidth();
- int h = mBitmap.getHeight();
+ int w = mDrawable.getIntrinsicWidth();
+ int h = mDrawable.getIntrinsicHeight();
Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
if (dr instanceof AdaptiveIconDrawable) {
@@ -214,11 +217,11 @@
mBadge.setBounds(badgeBounds);
// Do not draw the background in case of folder as its translucent
- mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
+ mShouldDraw = !(dr instanceof FolderAdaptiveIcon);
try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
Drawable nDr; // drawable to be normalized
- if (mDrawBitmap) {
+ if (mShouldDraw) {
nDr = dr;
} else {
// Since we just want the scale, avoid heavy drawing operations
@@ -308,7 +311,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
+ setMeasuredDimension(mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
}
/** Sets the scale of the view over the normal workspace icon size. */
@@ -352,29 +355,37 @@
return mDragRegion;
}
- public Bitmap getPreviewBitmap() {
- return mBitmap;
- }
-
@Override
protected void onDraw(Canvas canvas) {
mHasDrawn = true;
- if (mDrawBitmap) {
+ if (mShouldDraw) {
// Always draw the bitmap to mask anti aliasing due to clipPath
- boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
+ boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeDrawable != null;
if (crossFade) {
int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
mPaint.setAlpha(alpha);
}
- canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+ mDrawable.setColorFilter(mPaint.getColorFilter());
+ mDrawable.setAlpha(mPaint.getAlpha());
+ mDrawable.setBounds(
+ new Rect(0, 0, mDrawable.getIntrinsicWidth(),
+ mDrawable.getIntrinsicHeight()));
+ mDrawable.draw(canvas);
if (crossFade) {
mPaint.setAlpha((int) (255 * mCrossFadeProgress));
final int saveCount = canvas.save();
- float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
- float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
+ float sX = ((float) mDrawable.getIntrinsicWidth())
+ / mCrossFadeDrawable.getIntrinsicWidth();
+ float sY = ((float) mDrawable.getIntrinsicHeight())
+ / mCrossFadeDrawable.getIntrinsicHeight();
canvas.scale(sX, sY);
- canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
+ mCrossFadeDrawable.setColorFilter(mPaint.getColorFilter());
+ mCrossFadeDrawable.setAlpha(mPaint.getAlpha());
+ mDrawable.setBounds(
+ new Rect(0, 0, mDrawable.getIntrinsicWidth(),
+ mDrawable.getIntrinsicHeight()));
+ mCrossFadeDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
@@ -390,8 +401,8 @@
}
}
- public void setCrossFadeBitmap(Bitmap crossFadeBitmap) {
- mCrossFadeBitmap = crossFadeBitmap;
+ public void setCrossFadeDrawable(Drawable crossFadeDrawable) {
+ mCrossFadeDrawable = crossFadeDrawable;
}
public void crossFade(int duration) {
@@ -469,8 +480,8 @@
// Start the pick-up animation
DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
- lp.width = mBitmap.getWidth();
- lp.height = mBitmap.getHeight();
+ lp.width = mDrawable.getIntrinsicWidth();
+ lp.height = mDrawable.getIntrinsicHeight();
lp.customPosition = true;
setLayoutParams(lp);
move(touchX, touchY);
@@ -550,6 +561,11 @@
return mInitialScale;
}
+ /** Returns the current {@link Drawable} that is rendered in this view. */
+ public Drawable getDrawable() {
+ return mDrawable;
+ }
+
private static class SpringFloatValue {
private static final FloatPropertyCompat<SpringFloatValue> VALUE =
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 2290473..9f12e6e 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -96,7 +96,7 @@
PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
- dragHelper.setPreview(getPreview(mRequest));
+ dragHelper.setRemoteViewsPreview(getPreview(mRequest));
}
return dragHelper;
}
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index f543e47..29e7c18 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -28,7 +28,6 @@
import android.os.Build;
import android.os.Process;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
@@ -78,7 +77,7 @@
Drawable d = mContext.getSystemService(LauncherApps.class)
.getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
if (d == null) {
- d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle()));
+ d = cache.getDefaultIcon(Process.myUserHandle()).newIcon(mContext);
}
return d;
}
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 37200a6..6325877 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -56,9 +56,8 @@
if (mScreen != null) {
// Snap to the screen that we are hovering over now
Workspace w = mLauncher.getWorkspace();
- int page = w.indexOfChild(mScreen);
- if (page != w.getCurrentPage()) {
- w.snapToPage(page);
+ if (!w.isVisible(mScreen)) {
+ w.snapToPage(w.indexOfChild(mScreen));
}
} else {
mLauncher.getDragController().cancelDrag();
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 5954efa..c67efef 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -97,7 +97,7 @@
double thetaShift = 0;
if (curNumItems == 3) {
- thetaShift = Math.PI / 6;
+ thetaShift = Math.PI / 2;
} else if (curNumItems == 4) {
thetaShift = Math.PI / 4;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 471f671..a1b7997 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -695,6 +696,9 @@
mPageIndicator.stopAllAnimations();
startAnimation(anim);
+ // Because t=0 has the folder match the folder icon, we can skip the
+ // first frame and have the same movement one frame earlier.
+ anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 0235dfa..c1f4643 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -483,7 +483,7 @@
icon.verifyHighRes();
// Set the callback back to the actual icon, in case
// it was captured by the FolderIcon
- Drawable d = icon.getCompoundDrawables()[1];
+ Drawable d = icon.getIcon();
if (d != null) {
d.setCallback(icon);
}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 9ae7faf..8244f01 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,7 +16,6 @@
package com.android.launcher3.folder;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
@@ -33,10 +32,10 @@
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.view.View;
-import android.widget.TextView;
import androidx.annotation.NonNull;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -113,7 +112,7 @@
}
Drawable prepareCreateAnimation(final View destView) {
- Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
+ Drawable animateDrawable = ((BubbleTextView) destView).getIcon();
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
destView.getMeasuredWidth());
mReferenceDrawable = animateDrawable;
@@ -401,7 +400,7 @@
drawable.setLevel(item.getProgressLevel());
p.drawable = drawable;
} else {
- p.drawable = newIcon(mContext, item);
+ p.drawable = item.newIcon(mContext);
}
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
p.item = item;
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 21822a3..0e61b98 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -33,8 +33,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -88,10 +90,14 @@
}
/**
- * Returns a new bitmap to show when the {@link #mView} is being dragged around.
- * Responsibility for the bitmap is transferred to the caller.
+ * Returns a new drawable to show when the {@link #mView} is being dragged around.
+ * Responsibility for the drawable is transferred to the caller.
*/
- public Bitmap createDragBitmap() {
+ public Drawable createDrawable() {
+ if (mView instanceof LauncherAppWidgetHostView) {
+ return new AppWidgetHostViewDrawable((LauncherAppWidgetHostView) mView);
+ }
+
int width = 0;
int height = 0;
// Assume scaleX == scaleY, which is always the case for workspace items.
@@ -105,8 +111,9 @@
height = mView.getHeight();
}
- return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
- height + blurSizeOutline, (c) -> drawDragView(c, scale));
+ return new FastBitmapDrawable(
+ BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, scale)));
}
public final void generateDragOutline(Bitmap preview) {
@@ -129,7 +136,7 @@
return bounds;
}
- public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+ public float getScaleAndPosition(Drawable preview, int[] outPos) {
float scale = Launcher.getLauncher(mView.getContext())
.getDragLayer().getLocationInDragLayer(mView, outPos);
if (mView instanceof LauncherAppWidgetHostView) {
@@ -139,8 +146,8 @@
}
outPos[0] = Math.round(outPos[0] -
- (preview.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
- outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2
+ (preview.getIntrinsicWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
+ outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getIntrinsicHeight() / 2
- previewPadding / 2);
return scale;
}
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index cb42e7a..911f8c3 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -90,7 +90,10 @@
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) {
- result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
+ GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser));
+ if (option.visible) {
+ result.add(option);
+ }
}
}
} catch (IOException | XmlPullParserException e) {
diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java
index c0c3e5e..53303db 100644
--- a/src/com/android/launcher3/graphics/OverviewScrim.java
+++ b/src/com/android/launcher3/graphics/OverviewScrim.java
@@ -18,10 +18,6 @@
import static android.view.View.VISIBLE;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.graphics.Rect;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
@@ -55,15 +51,15 @@
public OverviewScrim(View view) {
super(view);
- mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
onExtractedColorsChanged(mWallpaperColorInfo);
}
- public void onInsetsChanged(Rect insets) {
- mStableScrimmedView = (OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0
- ? mLauncher.getHotseat()
- : mLauncher.getOverviewPanel();
+ /**
+ * Initializes once view hierarchy is established.
+ */
+ public void setup() {
+ mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
}
public void updateCurrentScrimmedView(ViewGroup root) {
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
deleted file mode 100644
index b6d25c4..0000000
--- a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.graphics;
-
-import static androidx.core.graphics.ColorUtils.compositeColors;
-
-import static com.android.launcher3.graphics.IconShape.getShapePath;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-import androidx.core.graphics.ColorUtils;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.util.Themes;
-
-/**
- * Subclass which draws a placeholder icon when the actual icon is not yet loaded
- */
-public class PlaceHolderIconDrawable extends FastBitmapDrawable {
-
- // Path in [0, 100] bounds.
- private final Path mProgressPath;
-
- public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
- super(info);
-
- mProgressPath = getShapePath();
- mPaint.setColor(compositeColors(
- Themes.getAttrColor(context, R.attr.loadingIconColor), info.color));
- }
-
- @Override
- protected void drawInternal(Canvas canvas, Rect bounds) {
- int saveCount = canvas.save();
- canvas.translate(bounds.left, bounds.top);
- canvas.scale(bounds.width() / 100f, bounds.height() / 100f);
- canvas.drawPath(mProgressPath, mPaint);
- canvas.restoreToCount(saveCount);
- }
-
- /** Updates this placeholder to {@code newIcon} with animation. */
- public void animateIconUpdate(Drawable newIcon) {
- int placeholderColor = mPaint.getColor();
- int originalAlpha = Color.alpha(placeholderColor);
-
- ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0);
- iconUpdateAnimation.setDuration(375);
- iconUpdateAnimation.addUpdateListener(valueAnimator -> {
- int newAlpha = (int) valueAnimator.getAnimatedValue();
- int newColor = ColorUtils.setAlphaComponent(placeholderColor, newAlpha);
-
- newIcon.setColorFilter(new PorterDuffColorFilter(newColor, PorterDuff.Mode.SRC_ATOP));
- });
- iconUpdateAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- newIcon.setColorFilter(null);
- }
- });
- iconUpdateAnimation.start();
- }
-
-}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index ce824df..ac0ec5f 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -37,8 +37,8 @@
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.Themes;
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
deleted file mode 100644
index 1bd252b..0000000
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2019 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.icons;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.FastBitmapDrawable;
-
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Wrapper over {@link AdaptiveIconDrawable} to intercept icon flattening logic for dynamic
- * clock icons
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender {
-
- private static final String TAG = "ClockDrawableWrapper";
-
- private static final boolean DISABLE_SECONDS = true;
-
- // Time after which the clock icon should check for an update. The actual invalidate
- // will only happen in case of any change.
- public static final long TICK_MS = DISABLE_SECONDS ? TimeUnit.MINUTES.toMillis(1) : 200L;
-
- private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
- private static final String ROUND_ICON_METADATA_KEY = LAUNCHER_PACKAGE
- + ".LEVEL_PER_TICK_ICON_ROUND";
- private static final String HOUR_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + ".HOUR_LAYER_INDEX";
- private static final String MINUTE_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
- + ".MINUTE_LAYER_INDEX";
- private static final String SECOND_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
- + ".SECOND_LAYER_INDEX";
- private static final String DEFAULT_HOUR_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_HOUR";
- private static final String DEFAULT_MINUTE_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_MINUTE";
- private static final String DEFAULT_SECOND_METADATA_KEY = LAUNCHER_PACKAGE
- + ".DEFAULT_SECOND";
-
- /* Number of levels to jump per second for the second hand */
- private static final int LEVELS_PER_SECOND = 10;
-
- public static final int INVALID_VALUE = -1;
-
- private final AnimationInfo mAnimationInfo = new AnimationInfo();
- private int mTargetSdkVersion;
-
- public ClockDrawableWrapper(AdaptiveIconDrawable base) {
- super(base.getBackground(), base.getForeground());
- }
-
- /**
- * Loads and returns the wrapper from the provided package, or returns null
- * if it is unable to load.
- */
- public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) {
- try {
- PackageManager pm = context.getPackageManager();
- ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
- PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
- final Bundle metadata = appInfo.metaData;
- if (metadata == null) {
- return null;
- }
- int drawableId = metadata.getInt(ROUND_ICON_METADATA_KEY, 0);
- if (drawableId == 0) {
- return null;
- }
-
- Drawable drawable = pm.getResourcesForApplication(appInfo).getDrawableForDensity(
- drawableId, iconDpi).mutate();
- if (!(drawable instanceof AdaptiveIconDrawable)) {
- return null;
- }
-
- ClockDrawableWrapper wrapper =
- new ClockDrawableWrapper((AdaptiveIconDrawable) drawable);
- wrapper.mTargetSdkVersion = appInfo.targetSdkVersion;
- AnimationInfo info = wrapper.mAnimationInfo;
-
- info.baseDrawableState = drawable.getConstantState();
-
- info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE);
- info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE);
- info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE);
-
- info.defaultHour = metadata.getInt(DEFAULT_HOUR_METADATA_KEY, 0);
- info.defaultMinute = metadata.getInt(DEFAULT_MINUTE_METADATA_KEY, 0);
- info.defaultSecond = metadata.getInt(DEFAULT_SECOND_METADATA_KEY, 0);
-
- LayerDrawable foreground = (LayerDrawable) wrapper.getForeground();
- int layerCount = foreground.getNumberOfLayers();
- if (info.hourLayerIndex < 0 || info.hourLayerIndex >= layerCount) {
- info.hourLayerIndex = INVALID_VALUE;
- }
- if (info.minuteLayerIndex < 0 || info.minuteLayerIndex >= layerCount) {
- info.minuteLayerIndex = INVALID_VALUE;
- }
- if (info.secondLayerIndex < 0 || info.secondLayerIndex >= layerCount) {
- info.secondLayerIndex = INVALID_VALUE;
- } else if (DISABLE_SECONDS) {
- foreground.setDrawable(info.secondLayerIndex, null);
- info.secondLayerIndex = INVALID_VALUE;
- }
- return wrapper;
- } catch (Exception e) {
- Log.d(TAG, "Unable to load clock drawable info", e);
- }
- return null;
- }
-
- @Override
- public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
- iconFactory.disableColorExtraction();
- float [] scale = new float[1];
- AdaptiveIconDrawable background = new AdaptiveIconDrawable(
- getBackground().getConstantState().newDrawable(), null);
- BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background,
- Process.myUserHandle(), mTargetSdkVersion, false, scale);
-
- return new ClockBitmapInfo(bitmap, color, scale[0], mAnimationInfo, bitmapInfo.icon);
- }
-
- @Override
- public void prepareToDrawOnUi() {
- mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground());
- }
-
- private static class AnimationInfo {
-
- public ConstantState baseDrawableState;
-
- public int hourLayerIndex;
- public int minuteLayerIndex;
- public int secondLayerIndex;
- public int defaultHour;
- public int defaultMinute;
- public int defaultSecond;
-
- boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) {
- time.setTimeInMillis(System.currentTimeMillis());
-
- // We need to rotate by the difference from the default time if one is specified.
- int convertedHour = (time.get(Calendar.HOUR) + (12 - defaultHour)) % 12;
- int convertedMinute = (time.get(Calendar.MINUTE) + (60 - defaultMinute)) % 60;
- int convertedSecond = (time.get(Calendar.SECOND) + (60 - defaultSecond)) % 60;
-
- boolean invalidate = false;
- if (hourLayerIndex != INVALID_VALUE) {
- final Drawable hour = foregroundDrawable.getDrawable(hourLayerIndex);
- if (hour.setLevel(convertedHour * 60 + time.get(Calendar.MINUTE))) {
- invalidate = true;
- }
- }
-
- if (minuteLayerIndex != INVALID_VALUE) {
- final Drawable minute = foregroundDrawable.getDrawable(minuteLayerIndex);
- if (minute.setLevel(time.get(Calendar.HOUR) * 60 + convertedMinute)) {
- invalidate = true;
- }
- }
-
- if (secondLayerIndex != INVALID_VALUE) {
- final Drawable second = foregroundDrawable.getDrawable(secondLayerIndex);
- if (second.setLevel(convertedSecond * LEVELS_PER_SECOND)) {
- invalidate = true;
- }
- }
-
- return invalidate;
- }
- }
-
- private static class ClockBitmapInfo extends BitmapInfo implements FastBitmapDrawable.Factory {
-
- public final float scale;
- public final int offset;
- public final AnimationInfo animInfo;
- public final Bitmap mFlattenedBackground;
-
- ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo,
- Bitmap background) {
- super(icon, color);
- this.scale = scale;
- this.animInfo = animInfo;
- this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth());
- this.mFlattenedBackground = background;
- }
-
- @Override
- public FastBitmapDrawable newDrawable() {
- return new ClockIconDrawable(this);
- }
- }
-
- private static class ClockIconDrawable extends FastBitmapDrawable implements Runnable {
-
- private final Calendar mTime = Calendar.getInstance();
-
- private final ClockBitmapInfo mInfo;
-
- private final AdaptiveIconDrawable mFullDrawable;
- private final LayerDrawable mForeground;
-
- ClockIconDrawable(ClockBitmapInfo clockInfo) {
- super(clockInfo);
-
- mInfo = clockInfo;
-
- mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState.newDrawable();
- mForeground = (LayerDrawable) mFullDrawable.getForeground();
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mFullDrawable.setBounds(bounds);
- }
-
- @Override
- public void drawInternal(Canvas canvas, Rect bounds) {
- if (mInfo == null) {
- super.drawInternal(canvas, bounds);
- return;
- }
- // draw the background that is already flattened to a bitmap
- canvas.drawBitmap(mInfo.mFlattenedBackground, null, bounds, mPaint);
-
- // prepare and draw the foreground
- mInfo.animInfo.applyTime(mTime, mForeground);
-
- canvas.scale(mInfo.scale, mInfo.scale,
- bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset);
- canvas.clipPath(mFullDrawable.getIconMask());
- mForeground.draw(canvas);
-
- reschedule();
- }
-
- @Override
- protected void updateFilter() {
- super.updateFilter();
- mFullDrawable.setColorFilter(mPaint.getColorFilter());
- }
-
- @Override
- public void run() {
- if (mInfo.animInfo.applyTime(mTime, mForeground)) {
- invalidateSelf();
- } else {
- reschedule();
- }
- }
-
- @Override
- public boolean setVisible(boolean visible, boolean restart) {
- boolean result = super.setVisible(visible, restart);
- if (visible) {
- reschedule();
- } else {
- unscheduleSelf(this);
- }
- return result;
- }
-
- private void reschedule() {
- if (!isVisible()) {
- return;
- }
-
- unscheduleSelf(this);
- final long upTime = SystemClock.uptimeMillis();
- final long step = TICK_MS; /* tick every 200 ms */
- scheduleSelf(this, upTime - ((upTime % step)) + step);
- }
-
- @Override
- public ConstantState getConstantState() {
- return new ClockConstantState(mInfo, isDisabled());
- }
-
- private static class ClockConstantState extends FastBitmapConstantState {
-
- private final ClockBitmapInfo mInfo;
-
- ClockConstantState(ClockBitmapInfo info, boolean isDisabled) {
- super(info.icon, info.color, isDisabled);
- mInfo = info;
- }
-
- @Override
- public FastBitmapDrawable newDrawable() {
- ClockIconDrawable drawable = new ClockIconDrawable(mInfo);
- drawable.setIsDisabled(mIsDisabled);
- return drawable;
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/icons/IconProvider.java b/src/com/android/launcher3/icons/IconProvider.java
deleted file mode 100644
index 1468b27..0000000
--- a/src/com/android/launcher3/icons/IconProvider.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2019 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.icons;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo.Extender;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.SafeCloseable;
-
-import java.util.Calendar;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-
-/**
- * Class to handle icon loading from different packages
- */
-public class IconProvider {
-
- private static final String TAG = "IconProvider";
- private static final boolean DEBUG = false;
-
- private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons";
-
- private static final String SYSTEM_STATE_SEPARATOR = " ";
-
- // Default value returned if there are problems getting resources.
- private static final int NO_ID = 0;
-
- private static final BiFunction<LauncherActivityInfo, Integer, Drawable> LAI_LOADER =
- LauncherActivityInfo::getIcon;
-
- private static final BiFunction<ActivityInfo, PackageManager, Drawable> AI_LOADER =
- ActivityInfo::loadUnbadgedIcon;
-
-
- private final Context mContext;
- private final ComponentName mCalendar;
- private final ComponentName mClock;
-
- public IconProvider(Context context) {
- mContext = context;
- mCalendar = parseComponentOrNull(context, R.string.calendar_component_name);
- mClock = parseComponentOrNull(context, R.string.clock_component_name);
- }
-
- /**
- * Adds any modification to the provided systemState for dynamic icons. This system state
- * is used by caches to check for icon invalidation.
- */
- public String getSystemStateForPackage(String systemState, String packageName) {
- if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
- return systemState + SYSTEM_STATE_SEPARATOR + getDay();
- } else {
- return systemState;
- }
- }
-
- /**
- * Loads the icon for the provided LauncherActivityInfo such that it can be drawn directly
- * on the UI
- */
- public Drawable getIconForUI(LauncherActivityInfo info, int iconDpi) {
- Drawable icon = getIcon(info, iconDpi);
- if (icon instanceof BitmapInfo.Extender) {
- ((Extender) icon).prepareToDrawOnUi();
- }
- return icon;
- }
-
- /**
- * Loads the icon for the provided LauncherActivityInfo
- */
- public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
- return getIcon(info.getApplicationInfo().packageName, info.getUser(),
- info, iconDpi, LAI_LOADER);
- }
-
- /**
- * Loads the icon for the provided activity info
- */
- public Drawable getIcon(ActivityInfo info, UserHandle user) {
- return getIcon(info.applicationInfo.packageName, user, info, mContext.getPackageManager(),
- AI_LOADER);
- }
-
- private <T, P> Drawable getIcon(String packageName, UserHandle user, T obj, P param,
- BiFunction<T, P, Drawable> loader) {
- Drawable icon = null;
- if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
- icon = loadCalendarDrawable(0);
- } else if (mClock != null
- && mClock.getPackageName().equals(packageName)
- && Process.myUserHandle().equals(user)) {
- icon = loadClockDrawable(0);
- }
- return icon == null ? loader.apply(obj, param) : icon;
- }
-
- private Drawable loadCalendarDrawable(int iconDpi) {
- PackageManager pm = mContext.getPackageManager();
- try {
- final Bundle metadata = pm.getActivityInfo(
- mCalendar,
- PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA)
- .metaData;
- final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName());
- final int id = getDynamicIconId(metadata, resources);
- if (id != NO_ID) {
- if (DEBUG) Log.d(TAG, "Got icon #" + id);
- return resources.getDrawableForDensity(id, iconDpi, null /* theme */);
- }
- } catch (PackageManager.NameNotFoundException e) {
- if (DEBUG) {
- Log.d(TAG, "Could not get activityinfo or resources for package: "
- + mCalendar.getPackageName());
- }
- }
- return null;
- }
-
- private Drawable loadClockDrawable(int iconDpi) {
- return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi);
- }
-
- protected boolean isClockIcon(ComponentKey key) {
- return mClock != null && mClock.equals(key.componentName)
- && Process.myUserHandle().equals(key.user);
- }
-
- /**
- * @param metadata metadata of the default activity of Calendar
- * @param resources from the Calendar package
- * @return the resource id for today's Calendar icon; 0 if resources cannot be found.
- */
- private int getDynamicIconId(Bundle metadata, Resources resources) {
- if (metadata == null) {
- return NO_ID;
- }
- String key = mCalendar.getPackageName() + ICON_METADATA_KEY_PREFIX;
- final int arrayId = metadata.getInt(key, NO_ID);
- if (arrayId == NO_ID) {
- return NO_ID;
- }
- try {
- return resources.obtainTypedArray(arrayId).getResourceId(getDay(), NO_ID);
- } catch (Resources.NotFoundException e) {
- if (DEBUG) {
- Log.d(TAG, "package defines '" + key + "' but corresponding array not found");
- }
- return NO_ID;
- }
- }
-
- /**
- * @return Today's day of the month, zero-indexed.
- */
- private int getDay() {
- return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1;
- }
-
-
- /**
- * Registers a callback to listen for calendar icon changes.
- * The callback receives the packageName for the calendar icon
- */
- public static SafeCloseable registerIconChangeListener(Context context,
- BiConsumer<String, UserHandle> callback, Handler handler) {
- ComponentName calendar = parseComponentOrNull(context, R.string.calendar_component_name);
- ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
-
- if (calendar == null && clock == null) {
- return () -> { };
- }
-
- BroadcastReceiver receiver = new DateTimeChangeReceiver(callback);
- final IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
- if (calendar != null) {
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_DATE_CHANGED);
- }
- context.registerReceiver(receiver, filter, null, handler);
-
- return () -> context.unregisterReceiver(receiver);
- }
-
- private static class DateTimeChangeReceiver extends BroadcastReceiver {
-
- private final BiConsumer<String, UserHandle> mCallback;
-
- DateTimeChangeReceiver(BiConsumer<String, UserHandle> callback) {
- mCallback = callback;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
- ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
- if (clock != null) {
- mCallback.accept(clock.getPackageName(), Process.myUserHandle());
- }
- }
-
- ComponentName calendar =
- parseComponentOrNull(context, R.string.calendar_component_name);
- if (calendar != null) {
- for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
- mCallback.accept(calendar.getPackageName(), user);
- }
- }
-
- }
- }
-
- private static ComponentName parseComponentOrNull(Context context, int resId) {
- String cn = context.getString(resId);
- return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
-
- }
-}
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d95e708..76b2ab0 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -23,6 +23,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -216,4 +217,14 @@
* @return a copy of this
*/
public abstract ItemInfoWithIcon clone();
+
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public FastBitmapDrawable newIcon(Context context) {
+ FastBitmapDrawable drawable = bitmap.newIcon(context);
+ drawable.setIsDisabled(isDisabled());
+ return drawable;
+ }
}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 80eeb22..d27d8c7 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -22,7 +22,6 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -86,9 +85,8 @@
mIsIconLarge = true;
}
if (mIconDrawable == null) {
- mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
- .getInstance(context).getIconCache()
- .getDefaultIcon(statusBarNotification.getUser()).icon);
+ mIconDrawable = LauncherAppState.getInstance(context).getIconCache()
+ .getDefaultIcon(statusBarNotification.getUser()).newIcon(context);
}
intent = notification.contentIntent;
autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 0320aa3..e954480 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -21,10 +21,13 @@
import android.app.Notification;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.Outline;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
import android.widget.TextView;
import com.android.launcher3.R;
@@ -43,7 +46,8 @@
private static final Rect sTempRect = new Rect();
private final Context mContext;
- private final PopupContainerWithArrow mContainer;
+ private final PopupContainerWithArrow mPopupContainer;
+ private final ViewGroup mRootView;
private final TextView mHeaderText;
private final TextView mHeaderCount;
@@ -53,7 +57,6 @@
private final View mIconView;
private final View mHeader;
- private final View mDivider;
private View mGutter;
@@ -61,8 +64,9 @@
private boolean mAnimatingNextIcon;
private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT;
- public NotificationItemView(PopupContainerWithArrow container) {
- mContainer = container;
+ public NotificationItemView(PopupContainerWithArrow container, ViewGroup rootView) {
+ mPopupContainer = container;
+ mRootView = rootView;
mContext = container.getContext();
mHeaderText = container.findViewById(R.id.notification_text);
@@ -72,17 +76,29 @@
mIconView = container.findViewById(R.id.popup_item_icon);
mHeader = container.findViewById(R.id.header);
- mDivider = container.findViewById(R.id.divider);
mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
mMainView.setSwipeDetector(mSwipeDetector);
mFooter.setContainer(this);
+
+ float radius = Themes.getDialogCornerRadius(mContext);
+ rootView.setClipToOutline(true);
+ rootView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
+ }
+ });
+ }
+
+ public void updateBackgroundColor(int color) {
+ mMainView.updateBackgroundColor(color);
}
public void addGutter() {
if (mGutter == null) {
- mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer);
+ mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
}
}
@@ -94,9 +110,8 @@
}
public void removeFooter() {
- if (mContainer.indexOfChild(mFooter) >= 0) {
- mContainer.removeView(mFooter);
- mContainer.removeView(mDivider);
+ if (mRootView.indexOfChild(mFooter) >= 0) {
+ mRootView.removeView(mFooter);
}
}
@@ -108,16 +123,15 @@
}
public void removeAllViews() {
- mContainer.removeView(mMainView);
- mContainer.removeView(mHeader);
+ mRootView.removeView(mMainView);
+ mRootView.removeView(mHeader);
- if (mContainer.indexOfChild(mFooter) >= 0) {
- mContainer.removeView(mFooter);
- mContainer.removeView(mDivider);
+ if (mRootView.indexOfChild(mFooter) >= 0) {
+ mRootView.removeView(mFooter);
}
if (mGutter != null) {
- mContainer.removeView(mGutter);
+ mRootView.removeView(mGutter);
}
}
@@ -136,11 +150,11 @@
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- sTempRect.set(mMainView.getLeft(), mMainView.getTop(),
- mMainView.getRight(), mMainView.getBottom());
+ sTempRect.set(mRootView.getLeft(), mRootView.getTop(),
+ mRootView.getRight(), mRootView.getBottom());
mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY());
if (!mIgnoreTouch) {
- mContainer.getParent().requestDisallowInterceptTouchEvent(true);
+ mPopupContainer.getParent().requestDisallowInterceptTouchEvent(true);
}
}
if (mIgnoreTouch) {
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 9b06523..c995666 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -97,15 +97,24 @@
super.onFinishInflate();
mTextAndBackground = findViewById(R.id.text_and_background);
- ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
- mBackgroundColor = colorBackground.getColor();
- RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
- Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
- colorBackground, null);
- mTextAndBackground.setBackground(rippleBackground);
mTitleView = mTextAndBackground.findViewById(R.id.title);
mTextView = mTextAndBackground.findViewById(R.id.text);
mIconView = findViewById(R.id.popup_item_icon);
+
+ ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
+ updateBackgroundColor(colorBackground.getColor());
+ }
+
+ public void updateBackgroundColor(int color) {
+ mBackgroundColor = color;
+ RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
+ Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
+ new ColorDrawable(color), null);
+ mTextAndBackground.setBackground(rippleBackground);
+ if (mNotificationInfo != null) {
+ mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
+ mBackgroundColor));
+ }
}
public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 15915e5..3736538 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -17,10 +17,12 @@
package com.android.launcher3.popup;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -28,6 +30,9 @@
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
@@ -48,6 +53,7 @@
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -61,6 +67,9 @@
*/
public abstract class ArrowPopup<T extends BaseDraggingActivity> extends AbstractFloatingView {
+ // +1 for system shortcut view
+ private static final int MAX_NUM_CHILDREN = MAX_SHORTCUTS + 1;
+
private final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
@@ -75,6 +84,8 @@
private final int mArrowPointRadius;
private final View mArrow;
+ private final int mMargin;
+
protected boolean mIsLeftAligned;
protected boolean mIsAboveIcon;
private int mGravity;
@@ -84,8 +95,14 @@
private final Rect mStartRect = new Rect();
private final Rect mEndRect = new Rect();
+ private final GradientDrawable mRoundedTop;
+ private final GradientDrawable mRoundedBottom;
+
private Runnable mOnCloseCallback = () -> { };
+ private int mArrowColor;
+ private final int[] mColors;
+
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
@@ -103,6 +120,7 @@
// Initialize arrow view
final Resources resources = getResources();
+ mMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
mArrow = new View(context);
@@ -111,6 +129,25 @@
mArrowOffsetHorizontal = resources.getDimensionPixelSize(
R.dimen.popup_arrow_horizontal_center_offset) - (mArrowWidth / 2);
mArrowPointRadius = resources.getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+
+ int smallerRadius = resources.getDimensionPixelSize(R.dimen.popup_smaller_radius);
+ mRoundedTop = new GradientDrawable();
+ mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
+ mOutlineRadius, smallerRadius, smallerRadius, smallerRadius, smallerRadius});
+
+ mRoundedBottom = new GradientDrawable();
+ mRoundedBottom.setCornerRadii(new float[] { smallerRadius, smallerRadius, smallerRadius,
+ smallerRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius});
+
+ int primaryColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
+ int secondaryColor = Themes.getAttrColor(context, R.attr.popupColorSecondary);
+ ArgbEvaluator argb = new ArgbEvaluator();
+ mColors = new int[MAX_NUM_CHILDREN];
+ // Interpolate between the two colors, exclusive.
+ float step = 1f / (MAX_NUM_CHILDREN + 1);
+ for (int i = 0; i < mColors.length; ++i) {
+ mColors[i] = (int) argb.evaluate((i + 1) * step, primaryColor, secondaryColor);
+ }
}
public ArrowPopup(Context context, AttributeSet attrs) {
@@ -154,6 +191,77 @@
protected void onInflationComplete(boolean isReversed) { }
/**
+ * Set the margins and radius of backgrounds after views are properly ordered.
+ */
+ protected void assignMarginsAndBackgrounds() {
+ int count = getChildCount();
+ int totalVisibleShortcuts = 0;
+ for (int i = 0; i < count; i++) {
+ View view = getChildAt(i);
+ if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
+ totalVisibleShortcuts++;
+ }
+ }
+
+ int numVisibleShortcut = 0;
+ View lastView = null;
+ int numVisibleChild = 0;
+ for (int i = 0; i < count; i++) {
+ View view = getChildAt(i);
+ boolean isShortcut = view instanceof DeepShortcutView;
+ if (view.getVisibility() == VISIBLE) {
+ if (lastView != null) {
+ MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+ mlp.bottomMargin = mMargin;
+ }
+ lastView = view;
+ MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+ mlp.bottomMargin = 0;
+
+ if (isShortcut) {
+ if (totalVisibleShortcuts == 1) {
+ view.setBackgroundResource(R.drawable.single_item_primary);
+ } else if (totalVisibleShortcuts > 1) {
+ if (numVisibleShortcut == 0) {
+ view.setBackground(mRoundedTop);
+ } else if (numVisibleShortcut == (totalVisibleShortcuts - 1)) {
+ view.setBackground(mRoundedBottom);
+ } else {
+ view.setBackgroundResource(R.drawable.middle_item_primary);
+ }
+ numVisibleShortcut++;
+ }
+ }
+
+ int color = mColors[numVisibleChild % mColors.length];
+ setChildColor(view, color);
+
+ // Arrow color matches the first child or the last child.
+ if (!mIsAboveIcon && numVisibleChild == 0) {
+ mArrowColor = color;
+ } else if (mIsAboveIcon) {
+ mArrowColor = color;
+ }
+
+ numVisibleChild++;
+ }
+ }
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ }
+
+ /**
+ * Sets the background color of the child.
+ */
+ protected void setChildColor(View view, int color) {
+ Drawable bg = view.getBackground();
+ if (bg instanceof GradientDrawable) {
+ ((GradientDrawable) bg.mutate()).setColor(color);
+ } else if (bg instanceof ColorDrawable) {
+ ((ColorDrawable) bg.mutate()).setColor(color);
+ }
+ }
+
+ /**
* Shows the popup at the desired location, optionally reversing the children.
* @param viewsToFlip number of views from the top to to flip in case of reverse order
*/
@@ -164,6 +272,7 @@
reverseOrder(viewsToFlip);
}
onInflationComplete(reverseOrder);
+ assignMarginsAndBackgrounds();
if (shouldAddArrow()) {
addArrow();
}
@@ -176,6 +285,7 @@
protected void show() {
setupForDisplay();
onInflationComplete(false);
+ assignMarginsAndBackgrounds();
if (shouldAddArrow()) {
addArrow();
}
@@ -203,8 +313,6 @@
for (int i = 0; i < count; i++) {
addView(allViews.get(i));
}
-
- orientAboutObject();
}
private int getArrowLeft() {
@@ -228,7 +336,7 @@
mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
mArrowOffsetHorizontal, -mArrowOffsetVertical,
!mIsAboveIcon, mIsLeftAligned,
- Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)));
+ mArrowColor));
mArrow.setElevation(getElevation());
}
@@ -274,10 +382,18 @@
private void orientAboutObject(boolean allowAlignLeft, boolean allowAlignRight) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- int width = getMeasuredWidth();
int extraVerticalSpace = mArrowHeight + mArrowOffsetVertical
+ getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
- int height = getMeasuredHeight() + extraVerticalSpace;
+ // The margins are added after we call this method, so we need to account for them here.
+ int numVisibleChildren = 0;
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ if (getChildAt(i).getVisibility() == VISIBLE) {
+ numVisibleChildren++;
+ }
+ }
+ int childMargins = (numVisibleChildren - 1) * mMargin;
+ int height = getMeasuredHeight() + extraVerticalSpace + childMargins;
+ int width = getMeasuredWidth();
getTargetObjectLocation(mTempRect);
InsettableFrameLayout dragLayer = getPopupContainer();
@@ -408,6 +524,7 @@
? getResources().getInteger(R.integer.config_popupArrowOpenCloseDuration)
: 0;
}
+
private void animateOpen() {
setVisibility(View.VISIBLE);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index a1ba747..c282ae8 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -60,6 +60,7 @@
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.notification.NotificationMainView;
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -91,6 +92,7 @@
private BubbleTextView mOriginalIcon;
private NotificationItemView mNotificationItemView;
private int mNumNotifications;
+ private ViewGroup mNotificationContainer;
private ViewGroup mSystemShortcutContainer;
@@ -169,6 +171,14 @@
return false;
}
+ @Override
+ protected void setChildColor(View v, int color) {
+ super.setChildColor(v, color);
+ if (v.getId() == R.id.notification_container && mNotificationItemView != null) {
+ mNotificationItemView.updateBackgroundColor(color);
+ }
+ }
+
/**
* Returns true if we can show the container.
*/
@@ -222,20 +232,6 @@
if (isReversed && mNotificationItemView != null) {
mNotificationItemView.inverseGutterMargin();
}
-
- // Update dividers
- int count = getChildCount();
- DeepShortcutView lastView = null;
- for (int i = 0; i < count; i++) {
- View view = getChildAt(i);
- if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
- if (lastView != null) {
- lastView.setDividerVisibility(VISIBLE);
- }
- lastView = (DeepShortcutView) view;
- lastView.setDividerVisibility(INVISIBLE);
- }
- }
}
@TargetApi(Build.VERSION_CODES.P)
@@ -257,8 +253,12 @@
// Add views
if (mNumNotifications > 0) {
// Add notification entries
- View.inflate(getContext(), R.layout.notification_content, this);
- mNotificationItemView = new NotificationItemView(this);
+ if (mNotificationContainer == null) {
+ mNotificationContainer = findViewById(R.id.notification_container);
+ mNotificationContainer.setVisibility(VISIBLE);
+ }
+ View.inflate(getContext(), R.layout.notification_content, mNotificationContainer);
+ mNotificationItemView = new NotificationItemView(this, mNotificationContainer);
if (mNumNotifications == 1) {
mNotificationItemView.removeFooter();
}
@@ -342,34 +342,11 @@
private void updateHiddenShortcuts() {
int allowedCount = mNotificationItemView != null
? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
- int originalHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
- int itemHeight = mNotificationItemView != null ?
- getResources().getDimensionPixelSize(R.dimen.bg_popup_item_condensed_height)
- : originalHeight;
- float iconScale = ((float) itemHeight) / originalHeight;
int total = mShortcuts.size();
for (int i = 0; i < total; i++) {
DeepShortcutView view = mShortcuts.get(i);
view.setVisibility(i >= allowedCount ? GONE : VISIBLE);
- view.getLayoutParams().height = itemHeight;
- view.getIconView().setScaleX(iconScale);
- view.getIconView().setScaleY(iconScale);
- }
- }
-
- private void updateDividers() {
- int count = getChildCount();
- DeepShortcutView lastView = null;
- for (int i = 0; i < count; i++) {
- View view = getChildAt(i);
- if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
- if (lastView != null) {
- lastView.setDividerVisibility(VISIBLE);
- }
- lastView = (DeepShortcutView) view;
- lastView.setDividerVisibility(INVISIBLE);
- }
}
}
@@ -591,8 +568,9 @@
// No more notifications, remove the notification views and expand all shortcuts.
mNotificationItemView.removeAllViews();
mNotificationItemView = null;
+ mNotificationContainer.setVisibility(GONE);
updateHiddenShortcuts();
- updateDividers();
+ assignMarginsAndBackgrounds();
} else {
mNotificationItemView.trimNotifications(
NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 7780894..6f9f0d7 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -40,6 +40,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -224,6 +225,7 @@
.map(recommendedWidget -> allWidgetItems.get(
new ComponentKey(recommendedWidget.getTargetComponent(),
recommendedWidget.user)))
+ .filter(Objects::nonNull)
.collect(Collectors.toList());
}
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
index 4653774..5b8d5bc 100644
--- a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -33,7 +33,7 @@
V newViewHolder(ViewGroup parent);
/** Populate UI references in {@link ViewHolder} with data. */
- void bindViewHolder(V viewHolder, T data);
+ void bindViewHolder(V viewHolder, T data, int position);
/**
* Called when the view is recycled. Views are recycled in batches once they are sufficiently
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index e9b92e2..1c1418c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -41,7 +41,6 @@
private BubbleTextView mBubbleText;
private View mIconView;
- private View mDivider;
private WorkspaceItemInfo mInfo;
private ShortcutInfo mDetail;
@@ -63,11 +62,6 @@
super.onFinishInflate();
mBubbleText = findViewById(R.id.bubble_text);
mIconView = findViewById(R.id.icon);
- mDivider = findViewById(R.id.divider);
- }
-
- public void setDividerVisibility(int visibility) {
- mDivider.setVisibility(visibility);
}
public BubbleTextView getBubbleText() {
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index 3e59b61..cecbb0d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -28,6 +28,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -42,15 +43,16 @@
}
@Override
- public Bitmap createDragBitmap() {
+ public Drawable createDrawable() {
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
- return BitmapRenderer.createHardwareBitmap(
- size + blurSizeOutline,
- size + blurSizeOutline,
- (c) -> drawDragViewOnBackground(c, size));
+ return new FastBitmapDrawable(
+ BitmapRenderer.createHardwareBitmap(
+ size + blurSizeOutline,
+ size + blurSizeOutline,
+ (c) -> drawDragViewOnBackground(c, size)));
} else {
- return createDragBitmapLegacy();
+ return new FastBitmapDrawable(createDragBitmapLegacy());
}
}
@@ -81,7 +83,7 @@
}
@Override
- public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+ public float getScaleAndPosition(Drawable preview, int[] outPos) {
Launcher launcher = Launcher.getLauncher(mView.getContext());
int iconSize = getDrawableBounds(mView.getBackground()).width();
float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
@@ -91,9 +93,10 @@
iconLeft = mView.getWidth() - iconSize - iconLeft;
}
- outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 +
- mPositionShift.x);
- outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2
+ outPos[0] += Math.round(
+ scale * iconLeft + (scale * iconSize - preview.getIntrinsicWidth()) / 2
+ + mPositionShift.x);
+ outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2
+ mPositionShift.y);
float size = launcher.getDeviceProfile().iconSizePx;
return scale * iconSize / size;
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index daec1d8..122573c 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -17,6 +17,8 @@
import android.content.Context;
+import com.android.launcher3.DeviceProfile;
+
/**
* Interface representing a state of a StatefulActivity
*/
@@ -52,4 +54,11 @@
* Returns if the state has the provided flag
*/
boolean hasFlag(int flagMask);
+
+ /**
+ * For this state, whether tasks should layout as a grid rather than a list.
+ */
+ default boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 51767e7..14ef2dc 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -18,7 +18,7 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -289,14 +289,14 @@
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
STATE_TYPE state, long duration) {
- return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
+ return createAnimationToNewWorkspace(state, duration, 0 /* animFlags */);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(
- STATE_TYPE state, long duration, @AnimationFlags int animComponents) {
+ STATE_TYPE state, long duration, @AnimationFlags int animFlags) {
StateAnimationConfig config = new StateAnimationConfig();
config.duration = duration;
- config.animFlags = animComponents;
+ config.animFlags = animFlags;
return createAnimationToNewWorkspace(state, config);
}
@@ -312,7 +312,7 @@
private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
PendingAnimation builder = new PendingAnimation(mConfig.duration);
- if (mConfig.getAnimComponents() != 0) {
+ if (!mConfig.hasAnimationFlag(SKIP_ALL_ANIMATIONS)) {
for (StateHandler handler : getStateHandlers()) {
handler.setStateWithAnimation(state, mConfig, builder);
}
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index fd1d965..76f89bc 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -31,7 +31,11 @@
| FLAG_HAS_SYS_UI_SCRIM;
public HintState(int id) {
- super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
+ this(id, LAUNCHER_STATE_HOME);
+ }
+
+ public HintState(int id, int statsLogOrdinal) {
+ super(id, statsLogOrdinal, STATE_FLAGS);
}
@Override
@@ -53,10 +57,4 @@
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(0.92f, 0, 0);
}
-
- @Override
- public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
- // Treat the QSB as part of the hotseat so they move together.
- return getHotseatScaleAndTranslation(launcher);
- }
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index fce8fff..d593013 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -59,10 +59,11 @@
float scale = grid.workspaceSpringLoadShrinkFactor;
Rect insets = launcher.getDragLayer().getInsets();
+ int insetsBottom = grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom;
float scaledHeight = scale * ws.getNormalChildHeight();
float shrunkTop = insets.top + grid.dropTargetBarSizePx;
- float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
+ float shrunkBottom = ws.getMeasuredHeight() - insetsBottom
- grid.workspacePadding.bottom
- grid.workspaceSpringLoadedBottomSpace;
float totalShrunkSpace = shrunkBottom - shrunkTop;
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index e4c67ee..8e7dcc0 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -27,34 +27,21 @@
*/
public class StateAnimationConfig {
- // We separate the state animations into "atomic" and "non-atomic" components. The atomic
- // components may be run atomically - that is, all at once, instead of user-controlled. However,
- // atomic components are not restricted to this purpose; they can be user-controlled alongside
- // non atomic components as well. Note that each gesture model has exactly one atomic component,
- // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
@IntDef(flag = true, value = {
- PLAY_NON_ATOMIC,
- PLAY_ATOMIC_OVERVIEW_SCALE,
- PLAY_ATOMIC_OVERVIEW_PEEK,
+ SKIP_ALL_ANIMATIONS,
SKIP_OVERVIEW,
SKIP_DEPTH_CONTROLLER,
- SKIP_TASKBAR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationFlags {}
- public static final int PLAY_NON_ATOMIC = 1 << 0;
- public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
- public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
- public static final int SKIP_OVERVIEW = 1 << 3;
- public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
- public static final int SKIP_TASKBAR = 1 << 5;
+ public static final int SKIP_ALL_ANIMATIONS = 1 << 0;
+ public static final int SKIP_OVERVIEW = 1 << 1;
+ public static final int SKIP_DEPTH_CONTROLLER = 1 << 2;
public long duration;
public boolean userControlled;
- public @AnimationFlags int animFlags = ANIM_ALL_COMPONENTS;
+ public @AnimationFlags int animFlags = 0;
- public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
- | PLAY_ATOMIC_OVERVIEW_PEEK;
// Various types of animation state transition
@IntDef(value = {
@@ -74,8 +61,6 @@
ANIM_OVERVIEW_MODAL,
ANIM_DEPTH,
ANIM_OVERVIEW_ACTIONS_FADE,
- ANIM_TASKBAR_FADE,
- ANIM_HOTSEAT_FADE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -95,10 +80,8 @@
public static final int ANIM_OVERVIEW_MODAL = 13;
public static final int ANIM_DEPTH = 14;
public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
- public static final int ANIM_TASKBAR_FADE = 16;
- public static final int ANIM_HOTSEAT_FADE = 17; // if not set, falls back to ANIM_WORKSPACE_FADE
- private static final int ANIM_TYPES_COUNT = 18;
+ private static final int ANIM_TYPES_COUNT = 16;
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
@@ -133,37 +116,9 @@
}
/**
- * @return Whether Overview is scaling as part of this animation. If this is the only
- * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
- * atomically, rather than being part of a normal state animation. StateHandlers can use
- * this to designate part of their animation that should scale with Overview.
- */
- public boolean playAtomicOverviewScaleComponent() {
- return hasAnimationFlag(StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE);
- }
-
- /**
- * @return Whether this animation will play atomically at the same time as a different,
- * user-controlled state transition. StateHandlers, which contribute to both animations, can
- * use this to avoid animating the same properties in both animations, since they'd conflict
- * with one another.
- */
- public boolean onlyPlayAtomicComponent() {
- return getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE
- || getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
- }
-
- /**
* Returns true if the config and any of the provided component flags
*/
public boolean hasAnimationFlag(@AnimationFlags int a) {
return (animFlags & a) != 0;
}
-
- /**
- * @return Only the flags that determine which animation components to play.
- */
- public @AnimationFlags int getAnimComponents() {
- return animFlags & StateAnimationConfig.ANIM_ALL_COMPONENTS;
- }
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index f34bff6..943d129 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -32,7 +32,8 @@
public static final int ALL_APPS_STATE_ORDINAL = 5;
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
public static final int HINT_STATE_ORDINAL = 7;
- public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 8;
+ public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8;
+ public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9;
public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static final String SEQUENCE_MAIN = "Main";
public static final String SEQUENCE_TIS = "TIS";
@@ -56,6 +57,8 @@
return "Background";
case HINT_STATE_ORDINAL:
return "Hint";
+ case HINT_STATE_TWO_BUTTON_ORDINAL:
+ return "Hint2Button";
case OVERVIEW_SPLIT_SELECT_ORDINAL:
return "OverviewSplitSelect";
default:
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 516fc74..2889801 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -27,30 +27,20 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
-import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
-import android.os.SystemClock;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
@@ -60,13 +50,6 @@
public abstract class AbstractStateChangeTouchController
implements TouchController, SingleAxisSwipeDetector.Listener {
- /**
- * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
- * TODO: Remove the atomic animation altogether and just go to OVERVIEW directly (b/175137718).
- */
- public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 1f;
- protected final long ATOMIC_DURATION = getAtomicDuration();
-
protected final Launcher mLauncher;
protected final SingleAxisSwipeDetector mDetector;
protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
@@ -89,23 +72,7 @@
private float mProgressMultiplier;
private float mDisplacementShift;
private boolean mCanBlockFling;
- private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
- protected AnimatorSet mAtomicAnim;
- // True if we want to resume playing atomic components when mAtomicAnim completes.
- private boolean mScheduleResumeAtomicComponent;
- private AutoPlayAtomicAnimationInfo mAtomicAnimAutoPlayInfo;
-
- private boolean mPassedOverviewAtomicThreshold;
- // mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
- // However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
- // atomic animation finishes, we only control the non-atomic components so that we don't
- // interfere with the atomic animation. When the atomic animation ends, we start controlling
- // the atomic components as well, using this controller.
- private AnimatorPlaybackController mAtomicComponentsController;
- private LauncherState mAtomicComponentsTargetState = NORMAL;
-
- private float mAtomicComponentsStartProgress;
+ private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
mLauncher = l;
@@ -113,10 +80,6 @@
mSwipeDirection = dir;
}
- protected long getAtomicDuration() {
- return 200;
- }
-
protected abstract boolean canInterceptTouch(MotionEvent ev);
@Override
@@ -182,7 +145,7 @@
protected abstract LauncherState getTargetState(LauncherState fromState,
boolean isDragTowardPositive);
- protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
+ protected abstract float initCurrentAnimation();
private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -199,28 +162,10 @@
mToState = newToState;
mStartProgress = 0;
- mPassedOverviewAtomicThreshold = false;
if (mCurrentAnimation != null) {
mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
}
- int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
- ? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS;
- mScheduleResumeAtomicComponent = false;
- if (mAtomicAnim != null) {
- animComponents = PLAY_NON_ATOMIC;
- // Control the non-atomic components until the atomic animation finishes, then control
- // the atomic components as well.
- mScheduleResumeAtomicComponent = true;
- }
- if (goingBetweenNormalAndOverview(mFromState, mToState)
- || mAtomicComponentsTargetState != mToState) {
- cancelAtomicComponentsController();
- }
-
- if (mAtomicComponentsController != null) {
- animComponents &= ~PLAY_ATOMIC_OVERVIEW_SCALE;
- }
- mProgressMultiplier = initCurrentAnimation(animComponents);
+ mProgressMultiplier = initCurrentAnimation();
mCurrentAnimation.dispatchOnStart();
return true;
}
@@ -231,13 +176,6 @@
protected void onReachedFinalState(LauncherState newToState) {
}
- protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
- LauncherState toState) {
- return (fromState == NORMAL || fromState == OVERVIEW)
- && (toState == NORMAL || toState == OVERVIEW)
- && mGoingBetweenStates;
- }
-
@Override
public void onDragStart(boolean start, float startDisplacement) {
mStartState = mLauncher.getStateManager().getState();
@@ -252,11 +190,6 @@
} else {
mCurrentAnimation.pause();
mStartProgress = mCurrentAnimation.getProgressFraction();
-
- mAtomicAnimAutoPlayInfo = null;
- if (mAtomicComponentsController != null) {
- mAtomicComponentsController.pause();
- }
}
mCanBlockFling = mFromState == NORMAL;
mFlingBlockCheck.unblockFling();
@@ -310,69 +243,6 @@
return;
}
mCurrentAnimation.setPlayFraction(fraction);
- if (mAtomicComponentsController != null) {
- // Make sure we don't divide by 0, and have at least a small runway.
- float start = Math.min(mAtomicComponentsStartProgress, 0.9f);
- mAtomicComponentsController.setPlayFraction((fraction - start) / (1 - start));
- }
- maybeUpdateAtomicAnim(mFromState, mToState, fraction);
- }
-
- /**
- * When going between normal and overview states, see if we passed the overview threshold and
- * play the appropriate atomic animation if so.
- */
- private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
- float progress) {
- if (!goingBetweenNormalAndOverview(fromState, toState)) {
- return;
- }
- boolean passedThreshold = progress >= ATOMIC_OVERVIEW_ANIM_THRESHOLD;
- if (passedThreshold != mPassedOverviewAtomicThreshold) {
- LauncherState atomicFromState = passedThreshold ? fromState: toState;
- LauncherState atomicToState = passedThreshold ? toState : fromState;
- mPassedOverviewAtomicThreshold = passedThreshold;
- if (mAtomicAnim != null) {
- mAtomicAnim.cancel();
- }
- mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
- mAtomicAnim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mAtomicAnim = null;
- mScheduleResumeAtomicComponent = false;
- }
-
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (!mScheduleResumeAtomicComponent) {
- return;
- }
- cancelAtomicComponentsController();
-
- if (mCurrentAnimation != null) {
- mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
- long duration = (long) (getShiftRange() * 2);
- mAtomicComponentsController = AnimatorPlaybackController.wrap(
- createAtomicAnimForState(mFromState, mToState, duration), duration);
- mAtomicComponentsController.dispatchOnStart();
- mAtomicComponentsTargetState = mToState;
- maybeAutoPlayAtomicComponentsAnim();
- }
- }
- });
- mAtomicAnim.start();
- mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- }
- }
-
- private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
- long duration) {
- StateAnimationConfig config = getConfigForStates(fromState, targetState);
- config.animFlags = PLAY_ATOMIC_OVERVIEW_SCALE;
- config.duration = duration;
- return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, config);
}
/**
@@ -451,59 +321,12 @@
mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState));
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(startProgress, endProgress);
- maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
- updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
- targetState, velocity, fling);
+ updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
mCurrentAnimation.dispatchOnStart();
if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
anim.start();
- mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
- maybeAutoPlayAtomicComponentsAnim();
- }
-
- /**
- * Animates the atomic components from the current progress to the final progress.
- *
- * Note that this only applies when we are controlling the atomic components separately from
- * the non-atomic components, which only happens if we reinit before the atomic animation
- * finishes.
- */
- private void maybeAutoPlayAtomicComponentsAnim() {
- if (mAtomicComponentsController == null || mAtomicAnimAutoPlayInfo == null) {
- return;
- }
-
- final AnimatorPlaybackController controller = mAtomicComponentsController;
- ValueAnimator atomicAnim = controller.getAnimationPlayer();
- atomicAnim.setFloatValues(controller.getProgressFraction(),
- mAtomicAnimAutoPlayInfo.toProgress);
- long duration = mAtomicAnimAutoPlayInfo.endTime - SystemClock.elapsedRealtime();
- mAtomicAnimAutoPlayInfo = null;
- if (duration <= 0) {
- atomicAnim.start();
- atomicAnim.end();
- mAtomicComponentsController = null;
- } else {
- atomicAnim.setDuration(duration);
- atomicAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mAtomicComponentsController == controller) {
- mAtomicComponentsController = null;
- }
- }
- });
- atomicAnim.start();
- }
- }
-
- private long getRemainingAtomicDuration() {
- if (mAtomicAnim == null) {
- return 0;
- }
- return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime();
}
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
@@ -513,10 +336,6 @@
}
protected void onSwipeInteractionCompleted(LauncherState targetState) {
- if (mAtomicComponentsController != null) {
- mAtomicComponentsController.getAnimationPlayer().end();
- mAtomicComponentsController = null;
- }
onReachedFinalState(mToState);
clearState();
boolean shouldGoToTargetState = mGoingBetweenStates || (mToState != targetState);
@@ -556,37 +375,12 @@
protected void clearState() {
cancelAnimationControllers();
- if (mAtomicAnim != null) {
- mAtomicAnim.cancel();
- mAtomicAnim = null;
- }
mGoingBetweenStates = true;
- mScheduleResumeAtomicComponent = false;
mDetector.finishedScrolling();
mDetector.setDetectableScrollConditions(0, false);
}
private void cancelAnimationControllers() {
mCurrentAnimation = null;
- cancelAtomicComponentsController();
- }
-
- private void cancelAtomicComponentsController() {
- if (mAtomicComponentsController != null) {
- mAtomicComponentsController.getAnimationPlayer().cancel();
- mAtomicComponentsController = null;
- }
- mAtomicAnimAutoPlayInfo = null;
- }
-
- private static class AutoPlayAtomicAnimationInfo {
-
- public final float toProgress;
- public final long endTime;
-
- AutoPlayAtomicAnimationInfo(float toProgress, long duration) {
- this.toProgress = toProgress;
- this.endTime = duration + SystemClock.elapsedRealtime();
- }
}
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index f9dcf2d..ab2652a 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,24 +23,18 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
/**
* TouchController to switch between NORMAL and ALL_APPS state.
*/
public class AllAppsSwipeController extends AbstractStateChangeTouchController {
- private MotionEvent mTouchDownEvent;
-
public AllAppsSwipeController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDownEvent = ev;
- }
if (mCurrentAnimation != null) {
// If we are already animating from a previous state, we can intercept.
return true;
@@ -69,11 +63,11 @@
}
@Override
- protected float initCurrentAnimation(@AnimationFlags int animComponents) {
+ protected float initCurrentAnimation() {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, maxAccuracy, animComponents);
+ .createAnimationToNewWorkspace(mToState, maxAccuracy);
float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
float totalShift = endVerticalShift - startVerticalShift;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index c1cf0c8..19dfe15 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -136,7 +136,7 @@
}
@Override
- public int getClearAllScrollOffset(View view, boolean isRtl) {
+ public int getClearAllSidePadding(View view, boolean isRtl) {
return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index fcfa205..9140a04 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -66,7 +66,7 @@
float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
int getMeasuredSize(View view);
float getPrimarySize(RectF rect);
- int getClearAllScrollOffset(View view, boolean isRtl);
+ int getClearAllSidePadding(View view, boolean isRtl);
int getSecondaryDimension(View view);
FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> getSecondaryViewTranslate();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 2bc2dc7..29be627 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -132,7 +132,7 @@
}
@Override
- public int getClearAllScrollOffset(View view, boolean isRtl) {
+ public int getClearAllSidePadding(View view, boolean isRtl) {
return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
}
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index c79b1f6..5be9529 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -42,49 +42,16 @@
}
};
- /**
- * Determines how each alpha should factor into the final alpha.
- */
- public enum Mode {
- BLEND(1f) {
- @Override
- public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
- return currentAlpha * otherAlpha;
- }
- },
-
- MAX(0f) {
- @Override
- public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
- return Math.max(currentAlpha, otherAlpha);
- }
- };
-
- Mode(float startAlpha) {
- mStartAlpha = startAlpha;
- }
-
- protected final float mStartAlpha;
- protected abstract float calculateNewAlpha(float currentAlpha, float otherAlpha);
- }
-
private final View mView;
private final AlphaProperty[] mMyProperties;
- private final Mode mMode;
private int mValidMask;
// Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
private boolean mUpdateVisibility;
public MultiValueAlpha(View view, int size) {
- this(view, size, Mode.BLEND);
- }
-
- public MultiValueAlpha(View view, int size, Mode mode) {
mView = view;
mMyProperties = new AlphaProperty[size];
- mMode = mode;
- mView.setAlpha(mMode.mStartAlpha);
mValidMask = 0;
for (int i = 0; i < size; i++) {
@@ -112,9 +79,9 @@
private final int mMyMask;
- private float mValue = mMode.mStartAlpha;
+ private float mValue = 1;
// Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
- private float mOthers = mMode.mStartAlpha;
+ private float mOthers = 1;
AlphaProperty(int myMask) {
mMyMask = myMask;
@@ -127,10 +94,10 @@
if ((mValidMask & mMyMask) == 0) {
// Our cache value is not correct, recompute it.
- mOthers = mMode.mStartAlpha;
+ mOthers = 1;
for (AlphaProperty prop : mMyProperties) {
if (prop != this) {
- mOthers = mMode.calculateNewAlpha(mOthers, prop.mValue);
+ mOthers *= prop.mValue;
}
}
}
@@ -140,7 +107,7 @@
mValidMask = mMyMask;
mValue = value;
- mView.setAlpha(mMode.calculateNewAlpha(mOthers, mValue));
+ mView.setAlpha(mOthers * mValue);
if (mUpdateVisibility) {
AlphaUpdateListener.updateVisibility(mView);
}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 512a286..e8a0635 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -28,6 +28,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
/**
@@ -92,10 +93,7 @@
}
public static int getAttrColor(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- return colorAccent;
+ return GraphicsUtils.getAttrColor(context, attr);
}
public static boolean getAttrBoolean(Context context, int attr) {
@@ -120,23 +118,6 @@
}
/**
- * Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
- */
- public static int getAlpha(Context context, int attr) {
- return (int) (255 * getFloat(context, attr, 0) + 0.5f);
- }
-
- /**
- * Returns the alpha corresponding to the theme attribute {@param attr}
- */
- public static float getFloat(Context context, int attr, float defValue) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- float value = ta.getFloat(0, defValue);
- ta.recycle();
- return value;
- }
-
- /**
* Scales a color matrix such that, when applied to color R G B A, it produces R' G' B' A' where
* R' = r * R
* G' = g * G
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 505c6ce..71aa4ac 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
+import android.view.LayoutInflater;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile;
@@ -67,13 +68,28 @@
}
/**
+ * Returns a LayoutInflater that is cloned in this Context, so that Views inflated by it will
+ * have the same Context. (i.e. {@link #lookupContext(Context)} will find this ActivityContext.)
+ */
+ default LayoutInflater getLayoutInflater() {
+ if (this instanceof Context) {
+ Context context = (Context) this;
+ return LayoutInflater.from(context).cloneInContext(context);
+ }
+ return null;
+ }
+
+ /**
* The root view to support drag-and-drop and popup support.
*/
BaseDragLayer getDragLayer();
DeviceProfile getDeviceProfile();
- static <T extends ActivityContext> T lookupContext(Context context) {
+ /**
+ * Returns the ActivityContext associated with the given Context.
+ */
+ static <T extends Context & ActivityContext> T lookupContext(Context context) {
if (context instanceof ActivityContext) {
return (T) context;
} else if (context instanceof ContextWrapper) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 1a114f3..9a2db10 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -134,7 +134,6 @@
(DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
view.getIconView().setBackgroundResource(item.iconRes);
view.getBubbleText().setText(item.labelRes);
- view.setDividerVisibility(View.INVISIBLE);
view.setOnClickListener(popup);
view.setOnLongClickListener(popup);
popup.mItemMap.put(view, item);
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index d0ec9d7..9701389 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -15,51 +15,25 @@
*/
package com.android.launcher3.views;
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.util.SparseBooleanArray;
-import android.view.View;
import android.widget.EdgeEffect;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
+import com.android.launcher3.Utilities;
+
+/**
+ * View group to allow rendering overscroll effect in a child at the parent level
+ */
public class SpringRelativeLayout extends RelativeLayout {
- private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
- private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
- private static final float VELOCITY_MULTIPLIER = 0.3f;
-
- private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
- new FloatPropertyCompat<SpringRelativeLayout>("value") {
-
- @Override
- public float getValue(SpringRelativeLayout object) {
- return object.mDampedScrollShift;
- }
-
- @Override
- public void setValue(SpringRelativeLayout object, float value) {
- object.setDampedScrollShift(value);
- }
- };
-
- protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
- private final SpringAnimation mSpring;
-
- private float mDampedScrollShift = 0;
- private SpringEdgeEffect mActiveEdge;
+ private final EdgeEffect mEdgeGlowTop;
+ private final EdgeEffect mEdgeGlowBottom;
public SpringRelativeLayout(Context context) {
this(context, null);
@@ -71,98 +45,73 @@
public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
- mSpring.setSpring(new SpringForce(0)
- .setStiffness(STIFFNESS)
- .setDampingRatio(DAMPING_RATIO));
- }
-
- public void addSpringView(int id) {
- mSpringViews.put(id, true);
- }
-
- public void removeSpringView(int id) {
- mSpringViews.delete(id);
- invalidate();
- }
-
- /**
- * Used to clip the canvas when drawing child views during overscroll.
- */
- public int getCanvasClipTopForOverscroll() {
- return 0;
+ mEdgeGlowTop = Utilities.ATLEAST_S
+ ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+ mEdgeGlowBottom = Utilities.ATLEAST_S
+ ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+ setWillNotDraw(false);
}
@Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
- int saveCount = canvas.save();
-
- canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
- canvas.translate(0, mDampedScrollShift);
- boolean result = super.drawChild(canvas, child, drawingTime);
-
- canvas.restoreToCount(saveCount);
-
- return result;
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (!mEdgeGlowTop.isFinished()) {
+ final int restoreCount = canvas.save();
+ canvas.translate(0, 0);
+ mEdgeGlowTop.setSize(getWidth(), getHeight());
+ if (mEdgeGlowTop.draw(canvas)) {
+ postInvalidateOnAnimation();
+ }
+ canvas.restoreToCount(restoreCount);
}
- return super.drawChild(canvas, child, drawingTime);
- }
-
- private void setActiveEdge(SpringEdgeEffect edge) {
- if (mActiveEdge != edge && mActiveEdge != null) {
- mActiveEdge.mDistance = 0;
- }
- mActiveEdge = edge;
- }
-
- protected void setDampedScrollShift(float shift) {
- if (shift != mDampedScrollShift) {
- mDampedScrollShift = shift;
- invalidate();
+ if (!mEdgeGlowBottom.isFinished()) {
+ final int restoreCount = canvas.save();
+ final int width = getWidth();
+ final int height = getHeight();
+ canvas.translate(-width, height);
+ canvas.rotate(180, width, 0);
+ mEdgeGlowBottom.setSize(width, height);
+ if (mEdgeGlowBottom.draw(canvas)) {
+ postInvalidateOnAnimation();
+ }
+ canvas.restoreToCount(restoreCount);
}
}
- private void finishScrollWithVelocity(float velocity) {
- mSpring.setStartVelocity(velocity);
- mSpring.setStartValue(mDampedScrollShift);
- mSpring.start();
- }
- protected void finishWithShiftAndVelocity(float shift, float velocity,
- DynamicAnimation.OnAnimationEndListener listener) {
- setDampedScrollShift(shift);
- mSpring.addEndListener(listener);
- finishScrollWithVelocity(velocity);
+ /**
+ * Absorbs the velocity as a result for swipe-up fling
+ */
+ protected void absorbSwipeUpVelocity(int velocity) {
+ mEdgeGlowBottom.onAbsorb(velocity);
+ invalidate();
}
public EdgeEffectFactory createEdgeEffectFactory() {
- return new SpringEdgeEffectFactory();
+ return new ProxyEdgeEffectFactory();
}
- private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+ private class ProxyEdgeEffectFactory extends EdgeEffectFactory {
@NonNull @Override
protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
switch (direction) {
case DIRECTION_TOP:
- return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
+ return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
case DIRECTION_BOTTOM:
- return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+ return new EdgeEffectProxy(getContext(), mEdgeGlowBottom);
}
return super.createEdgeEffect(view, direction);
}
}
- private class SpringEdgeEffect extends EdgeEffect {
+ private class EdgeEffectProxy extends EdgeEffect {
- private final float mVelocityMultiplier;
+ private final EdgeEffect mParent;
- private float mDistance;
-
- public SpringEdgeEffect(Context context, float velocityMultiplier) {
+ EdgeEffectProxy(Context context, EdgeEffect parent) {
super(context);
- mVelocityMultiplier = velocityMultiplier;
+ mParent = parent;
}
@Override
@@ -170,22 +119,44 @@
return false;
}
+ private void invalidateParentScrollEffect() {
+ if (!mParent.isFinished()) {
+ invalidate();
+ }
+ }
+
@Override
public void onAbsorb(int velocity) {
- finishScrollWithVelocity(velocity * mVelocityMultiplier);
+ mParent.onAbsorb(velocity);
+ invalidateParentScrollEffect();
+ }
+
+ @Override
+ public void onPull(float deltaDistance) {
+ mParent.onPull(deltaDistance);
+ invalidateParentScrollEffect();
}
@Override
public void onPull(float deltaDistance, float displacement) {
- setActiveEdge(this);
- mDistance += deltaDistance * (mVelocityMultiplier / 3f);
- setDampedScrollShift(mDistance * getHeight());
+ mParent.onPull(deltaDistance, displacement);
+ invalidateParentScrollEffect();
}
@Override
public void onRelease() {
- mDistance = 0;
- finishScrollWithVelocity(0);
+ mParent.onRelease();
+ invalidateParentScrollEffect();
+ }
+
+ @Override
+ public void finish() {
+ mParent.finish();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return mParent.isFinished();
}
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 4fe631a..fc63af0 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -108,17 +108,18 @@
// If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
// we abort the drag.
- if (image.getBitmap() == null) {
+ if (image.getDrawable() == null) {
return false;
}
PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
- dragHelper.setPreview(v.getPreview());
+ dragHelper.setRemoteViewsPreview(v.getPreview());
+ dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(image, loc);
- dragHelper.startDrag(image.getBitmapBounds(), image.getBitmap().getWidth(),
+ dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
close(true);
return true;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index df01295..687318f 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,10 +16,11 @@
package com.android.launcher3.widget;
-import android.app.WallpaperManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
@@ -32,12 +33,14 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.Advanceable;
import android.widget.RemoteViews;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Launcher;
@@ -50,6 +53,7 @@
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
import java.util.List;
@@ -74,7 +78,6 @@
private final CheckLongPressHelper mLongPressHelper;
protected final Launcher mLauncher;
private final Workspace mWorkspace;
- private final WallpaperManager mWallpaperManager;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mReinflateOnConfigChange;
@@ -85,12 +88,28 @@
private boolean mIsScrollable;
private boolean mIsAttachedToWindow;
private boolean mIsAutoAdvanceRegistered;
+ private boolean mIsInDragMode = false;
private Runnable mAutoAdvanceRunnable;
private RectF mLastLocationRegistered = null;
+ @Nullable private AppWidgetHostViewDragListener mDragListener;
+
// Used to store the widget size during onLayout.
private final Rect mCurrentWidgetSize = new Rect();
+ private final Rect mWidgetSizeAtDrag = new Rect();
private final RectF mTempRectF = new RectF();
private final boolean mIsRtl;
+ private final Rect mEnforcedRectangle = new Rect();
+ private final float mEnforcedCornerRadius;
+ private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
+ outline.setEmpty();
+ } else {
+ outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
+ }
+ }
+ };
public LauncherAppWidgetHostView(Context context) {
super(context);
@@ -106,9 +125,10 @@
setOnLightBackground(true);
}
mIsRtl = Utilities.isRtl(context.getResources());
- mWallpaperManager = WallpaperManager.getInstance(getContext());
mColorExtractor = LocalColorExtractor.newInstance(getContext());
mColorExtractor.setListener(this);
+
+ mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
}
@Override
@@ -121,9 +141,17 @@
}
@Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mIsInDragMode && mDragListener != null) {
+ mDragListener.onDragContentChanged();
+ }
+ }
+
+ @Override
public boolean onLongClick(View view) {
if (mIsScrollable) {
- DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+ DragLayer dragLayer = mLauncher.getDragLayer();
dragLayer.requestDisallowInterceptTouchEvent(false);
}
view.performLongClick();
@@ -158,7 +186,7 @@
if (viewGroup instanceof AdapterView) {
return true;
} else {
- for (int i=0; i < viewGroup.getChildCount(); i++) {
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup) {
if (checkScrollableRecursively((ViewGroup) child)) {
@@ -172,7 +200,7 @@
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+ DragLayer dragLayer = mLauncher.getDragLayer();
if (mIsScrollable) {
dragLayer.requestDisallowInterceptTouchEvent(true);
}
@@ -252,70 +280,91 @@
mIsScrollable = checkScrollableRecursively(this);
- mCurrentWidgetSize.left = left;
- mCurrentWidgetSize.top = top;
- mCurrentWidgetSize.right = right;
- mCurrentWidgetSize.bottom = bottom;
- updateColorExtraction(mCurrentWidgetSize);
+ if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
+ mCurrentWidgetSize.left = left;
+ mCurrentWidgetSize.top = top;
+ mCurrentWidgetSize.right = right;
+ mCurrentWidgetSize.bottom = bottom;
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+ int pageId = mWorkspace.getPageIndexForScreenId(info.screenId);
+ updateColorExtraction(mCurrentWidgetSize, pageId);
+ }
+
+ enforceRoundedCorners();
}
- private void updateColorExtraction(Rect widgetLocation) {
+ /** Starts the drag mode. */
+ public void startDrag(AppWidgetHostViewDragListener dragListener) {
+ mIsInDragMode = true;
+ mDragListener = dragListener;
+ }
+
+ /** Handles a drag event occurred on a workspace page, {@code pageId}. */
+ public void handleDrag(Rect rect, int pageId) {
+ mWidgetSizeAtDrag.set(rect);
+ updateColorExtraction(mWidgetSizeAtDrag, pageId);
+ }
+
+ /** Ends the drag mode. */
+ public void endDrag() {
+ mIsInDragMode = false;
+ mDragListener = null;
+ mWidgetSizeAtDrag.setEmpty();
+ requestLayout();
+ }
+
+ private void updateColorExtraction(Rect widgetLocation, int pageId) {
// If the widget hasn't been measured and laid out, we cannot do this.
if (widgetLocation.isEmpty()) {
return;
}
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
- if (info != null) {
- int screenWidth = mLauncher.getDeviceProfile().widthPx;
- int screenHeight = mLauncher.getDeviceProfile().heightPx;
- int numScreens = mWorkspace.getNumPagesForWallpaperParallax();
- int screenId = mIsRtl ? numScreens - info.screenId : info.screenId;
- float relativeScreenWidth = 1f / numScreens;
- float absoluteTop = widgetLocation.top;
- float absoluteBottom = widgetLocation.bottom;
- for (View v = (View) getParent();
- v != null && v.getId() != R.id.launcher;
- v = (View) v.getParent()) {
- absoluteBottom += v.getTop();
- absoluteTop += v.getTop();
- }
- float xOffset = 0;
- View parentView = (View) getParent();
- // The layout depends on the orientation.
- if (getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE) {
- xOffset = screenHeight - mWorkspace.getPaddingRight()
- - parentView.getWidth();
- } else {
- xOffset = mWorkspace.getPaddingLeft() + parentView.getPaddingLeft();
- }
- // This is the position of the widget relative to the wallpaper, as expected by the
- // local color extraction of the WallpaperManager.
- // The coordinate system is such that, on the horizontal axis, each screen has a
- // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
- // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
- // the position of the widget relative to the screen. For the vertical axis, this is
- // simply the location of the widget relative to the screen.
- mTempRectF.left = ((widgetLocation.left + xOffset) / screenWidth + screenId)
- * relativeScreenWidth;
- mTempRectF.right = ((widgetLocation.right + xOffset) / screenWidth + screenId)
- * relativeScreenWidth;
- mTempRectF.top = absoluteTop / screenHeight;
- mTempRectF.bottom = absoluteBottom / screenHeight;
- if (mTempRectF.left < 0 || mTempRectF.right > 1 || mTempRectF.top < 0
- || mTempRectF.bottom > 1) {
- Log.e(LOG_TAG, " Error, invalid relative position");
- return;
- }
- if (!mTempRectF.equals(mLastLocationRegistered)) {
- if (mLastLocationRegistered != null) {
- mColorExtractor.removeLocations();
- }
- mLastLocationRegistered = new RectF(mTempRectF);
- mColorExtractor.addLocation(List.of(mLastLocationRegistered));
- }
+ int screenWidth = mLauncher.getDeviceProfile().widthPx;
+ int screenHeight = mLauncher.getDeviceProfile().heightPx;
+ int numScreens = mWorkspace.getNumPagesForWallpaperParallax();
+ pageId = mIsRtl ? numScreens - pageId - 1 : pageId;
+ float relativeScreenWidth = 1f / numScreens;
+ float absoluteTop = widgetLocation.top;
+ float absoluteBottom = widgetLocation.bottom;
+ for (View v = (View) getParent();
+ v != null && v.getId() != R.id.launcher;
+ v = (View) v.getParent()) {
+ absoluteBottom += v.getTop();
+ absoluteTop += v.getTop();
+ }
+ float xOffset = 0;
+ View parentView = (View) getParent();
+ // The layout depends on the orientation.
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ int parentViewWidth = parentView == null ? 0 : parentView.getWidth();
+ xOffset = screenHeight - mWorkspace.getPaddingRight() - parentViewWidth;
} else {
- mColorExtractor.removeLocations();
+ int parentViewPaddingLeft = parentView == null ? 0 : parentView.getPaddingLeft();
+ xOffset = mWorkspace.getPaddingLeft() + parentViewPaddingLeft;
+ }
+ // This is the position of the widget relative to the wallpaper, as expected by the
+ // local color extraction of the WallpaperManager.
+ // The coordinate system is such that, on the horizontal axis, each screen has a
+ // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
+ // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
+ // the position of the widget relative to the screen. For the vertical axis, this is
+ // simply the location of the widget relative to the screen.
+ mTempRectF.left = ((widgetLocation.left + xOffset) / screenWidth + pageId)
+ * relativeScreenWidth;
+ mTempRectF.right = ((widgetLocation.right + xOffset) / screenWidth + pageId)
+ * relativeScreenWidth;
+ mTempRectF.top = absoluteTop / screenHeight;
+ mTempRectF.bottom = absoluteBottom / screenHeight;
+ if (mTempRectF.left < 0 || mTempRectF.right > 1 || mTempRectF.top < 0
+ || mTempRectF.bottom > 1) {
+ Log.e(LOG_TAG, " Error, invalid relative position");
+ return;
+ }
+ if (!mTempRectF.equals(mLastLocationRegistered)) {
+ if (mLastLocationRegistered != null) {
+ mColorExtractor.removeLocations();
+ }
+ mLastLocationRegistered = new RectF(mTempRectF);
+ mColorExtractor.addLocation(List.of(mLastLocationRegistered));
}
}
@@ -439,4 +488,40 @@
}
return false;
}
+
+ @UiThread
+ private void resetRoundedCorners() {
+ setOutlineProvider(ViewOutlineProvider.BACKGROUND);
+ setClipToOutline(false);
+ }
+
+ @UiThread
+ private void enforceRoundedCorners() {
+ if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
+ resetRoundedCorners();
+ return;
+ }
+ View background = RoundedCornerEnforcement.findBackground(this);
+ if (background == null
+ || RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
+ resetRoundedCorners();
+ return;
+ }
+ RoundedCornerEnforcement.computeRoundedRectangle(this,
+ background,
+ mEnforcedRectangle);
+ setOutlineProvider(mCornerRadiusEnforcementOutline);
+ setClipToOutline(true);
+ }
+
+ /** Returns the corner radius currently enforced, in pixels. */
+ public float getEnforcedCornerRadius() {
+ return mEnforcedCornerRadius;
+ }
+
+ /** Returns true if the corner radius are enforced for this App Widget. */
+ public boolean hasEnforcedCornerRadius() {
+ return getClipToOutline();
+ }
+
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index ce97d2e..ad61495 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -1,5 +1,7 @@
package com.android.launcher3.widget;
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -33,6 +35,8 @@
public int spanY;
public int minSpanX;
public int minSpanY;
+ public int maxSpanX;
+ public int maxSpanY;
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {
@@ -78,15 +82,40 @@
|| !idp.portraitProfile.shouldInsetWidgets()) {
AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
}
- spanX = Math.max(1, (int) Math.ceil(
- (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
- spanY = Math.max(1, (int) Math.ceil(
- (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
- minSpanX = Math.max(1, (int) Math.ceil(
- (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
- minSpanY = Math.max(1, (int) Math.ceil(
- (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
+ minSpanX = getSpanX(widgetPadding, minResizeWidth, smallestCellWidth);
+ minSpanY = getSpanY(widgetPadding, minResizeHeight, smallestCellHeight);
+
+ // Use maxResizeWidth/Height if they are defined and we're on S or above.
+ maxSpanX =
+ (ATLEAST_S && maxResizeWidth > 0)
+ ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)
+ : idp.numColumns;
+ maxSpanY =
+ (ATLEAST_S && maxResizeHeight > 0)
+ ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)
+ : idp.numRows;
+
+ // Use targetCellWidth/Height if it is within the min/max ranges and we're on S or above.
+ // Otherwise, fall back to minWidth/Height.
+ if (ATLEAST_S && targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
+ && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
+ spanX = targetCellWidth;
+ spanY = targetCellHeight;
+ } else {
+ spanX = getSpanX(widgetPadding, minWidth, smallestCellWidth);
+ spanY = getSpanY(widgetPadding, minHeight, smallestCellHeight);
+ }
+ }
+
+ private int getSpanX(Rect widgetPadding, int widgetWidth, float cellWidth) {
+ return Math.max(1, (int) Math.ceil(
+ (widgetWidth + widgetPadding.left + widgetPadding.right) / cellWidth));
+ }
+
+ private int getSpanY(Rect widgetPadding, int widgetHeight, float cellHeight) {
+ return Math.max(1, (int) Math.ceil(
+ (widgetHeight + widgetPadding.top + widgetPadding.bottom) / cellHeight));
}
public String getLabel(PackageManager packageManager) {
@@ -110,6 +139,10 @@
}
}
+ public boolean isReconfigurable() {
+ return configure != null && (getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) != 0;
+ }
+
@Override
public final ComponentName getComponent() {
return provider;
@@ -124,4 +157,4 @@
public Drawable getFullResIcon(IconCache cache) {
return cache.getFullResIcon(provider.getPackageName(), icon);
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 4b113d8..3308eec 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -16,7 +16,6 @@
package com.android.launcher3.widget;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import android.content.Context;
@@ -37,8 +36,8 @@
import android.widget.RemoteViews;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -152,12 +151,12 @@
// 2) Preload icon in the center
// 3) Setup icon in the center and app icon in the top right corner.
if (mDisabledForSafeMode) {
- FastBitmapDrawable disabledIcon = newIcon(getContext(), info);
+ FastBitmapDrawable disabledIcon = info.newIcon(getContext());
disabledIcon.setIsDisabled(true);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = newIcon(getContext(), info);
+ mCenterDrawable = info.newIcon(getContext());
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
updateSettingColor(info.bitmap.color);
} else {
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 6e83836..3e61e56 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -33,10 +33,14 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.RoundDrawableWrapper;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
/**
* Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -49,15 +53,29 @@
private final PendingAddItemInfo mAddInfo;
private int[] mEstimatedCellSize;
- @Nullable private RemoteViews mPreview;
+ @Nullable private RemoteViews mRemoteViewsPreview;
+ @Nullable private LauncherAppWidgetHostView mAppWidgetHostViewPreview;
+ private final float mEnforcedRoundedCornersForWidget;
public PendingItemDragHelper(View view) {
super(view);
mAddInfo = (PendingAddItemInfo) view.getTag();
+ mEnforcedRoundedCornersForWidget = RoundedCornerEnforcement.computeEnforcedRadius(
+ view.getContext());
}
- public void setPreview(@Nullable RemoteViews preview) {
- mPreview = preview;
+ /**
+ * Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in
+ * the pin widget flow.
+ */
+ public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview) {
+ mRemoteViewsPreview = remoteViewsPreview;
+ }
+
+ /** Sets a {@link LauncherAppWidgetHostView} which shows a preview layout of an app widget. */
+ public void setAppWidgetHostViewPreview(
+ @Nullable LauncherAppWidgetHostView appWidgetHostViewPreview) {
+ mAppWidgetHostViewPreview = appWidgetHostViewPreview;
}
/**
@@ -74,7 +92,7 @@
final Launcher launcher = Launcher.getLauncher(mView.getContext());
LauncherAppState app = LauncherAppState.getInstance(launcher);
- Bitmap preview = null;
+ Drawable preview = null;
final float scale;
final Point dragOffset;
final Rect dragRegion;
@@ -90,13 +108,25 @@
int[] previewSizeBeforeScale = new int[1];
- if (mPreview != null) {
- preview = WidgetCell.generateFromRemoteViews(launcher, mPreview,
- createWidgetInfo.info, maxWidth, previewSizeBeforeScale);
+ if (mRemoteViewsPreview != null) {
+ preview = new FastBitmapDrawable(
+ WidgetCell.generateFromRemoteViews(launcher, mRemoteViewsPreview,
+ createWidgetInfo.info, maxWidth, previewSizeBeforeScale));
+ }
+ if (mAppWidgetHostViewPreview != null) {
+ preview = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
+ launcher.getDragController()
+ .addDragListener(new AppWidgetHostViewDragListener(launcher));
}
if (preview == null) {
- preview = app.getWidgetCache().generateWidgetPreview(launcher,
- createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale).first;
+ Drawable p = new FastBitmapDrawable(
+ app.getWidgetCache().generateWidgetPreview(launcher,
+ createWidgetInfo.info, maxWidth, null,
+ previewSizeBeforeScale).first);
+ if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
+ p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
+ }
+ preview = p;
}
if (previewSizeBeforeScale[0] < previewBitmapWidth) {
@@ -109,7 +139,7 @@
previewBounds.left += padding;
previewBounds.right -= padding;
}
- scale = previewBounds.width() / (float) preview.getWidth();
+ scale = previewBounds.width() / (float) preview.getIntrinsicWidth();
launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView));
dragOffset = null;
@@ -119,9 +149,10 @@
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
LauncherIcons li = LauncherIcons.obtain(launcher);
- preview = li.createScaledBitmapWithoutShadow(icon, 0);
+ preview = new FastBitmapDrawable(
+ li.createScaledBitmapWithoutShadow(icon, 0));
li.recycle();
- scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
+ scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getIntrinsicWidth();
dragOffset = new Point(previewPadding / 2, previewPadding / 2);
@@ -149,9 +180,9 @@
launcher.getWorkspace().prepareDragWithProvider(this);
int dragLayerX = screenPos.x + previewBounds.left
- + (int) ((scale * preview.getWidth() - preview.getWidth()) / 2);
+ + (int) ((scale * preview.getIntrinsicWidth() - preview.getIntrinsicWidth()) / 2);
int dragLayerY = screenPos.y + previewBounds.top
- + (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
+ + (int) ((scale * preview.getIntrinsicHeight() - preview.getIntrinsicHeight()) / 2);
// Start the drag
launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
diff --git a/src/com/android/launcher3/widget/RoundedCornerEnforcement.java b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
new file mode 100644
index 0000000..1e46ffd
--- /dev/null
+++ b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities to compute the enforced the use of rounded corners on App Widgets.
+ */
+public class RoundedCornerEnforcement {
+ // This class is only a namespace and not meant to be instantiated.
+ private RoundedCornerEnforcement() {
+ }
+
+ /**
+ * Find the background view for a widget.
+ *
+ * @param appWidget the view containing the App Widget (typically the instance of
+ * {@link AppWidgetHostView}).
+ */
+ @Nullable
+ public static View findBackground(@NonNull View appWidget) {
+ List<View> backgrounds = findViewsWithId(appWidget, android.R.id.background);
+ if (backgrounds.size() == 1) {
+ return backgrounds.get(0);
+ }
+ // Really, the argument should contain the widget, so it cannot be the background.
+ if (appWidget instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) appWidget;
+ if (vg.getChildCount() > 0) {
+ return findUndefinedBackground(vg.getChildAt(0));
+ }
+ }
+ return appWidget;
+ }
+
+ /**
+ * Check whether the app widget has opted out of the enforcement.
+ */
+ public static boolean hasAppWidgetOptedOut(@NonNull View appWidget, @NonNull View background) {
+ return background.getId() == android.R.id.background && background.getClipToOutline();
+ }
+
+ /** Check if the app widget is in the deny list. */
+ public static boolean isRoundedCornerEnabled() {
+ return Utilities.ATLEAST_S && FeatureFlags.ENABLE_ENFORCED_ROUNDED_CORNERS.get();
+ }
+
+ /**
+ * Computes the rounded rectangle needed for this app widget.
+ *
+ * @param appWidget View onto which the rounded rectangle will be applied.
+ * @param background Background view. This must be either {@code appWidget} or a descendant
+ * of {@code appWidget}.
+ * @param outRect Rectangle set to the rounded rectangle coordinates, in the reference frame
+ * of {@code appWidget}.
+ */
+ public static void computeRoundedRectangle(@NonNull View appWidget, @NonNull View background,
+ @NonNull Rect outRect) {
+ outRect.left = 0;
+ outRect.right = background.getWidth();
+ outRect.top = 0;
+ outRect.bottom = background.getHeight();
+ while (background != appWidget) {
+ outRect.offset(background.getLeft(), background.getTop());
+ background = (View) background.getParent();
+ }
+ }
+
+ /**
+ * Computes the radius of the rounded rectangle that should be applied to a widget expanded
+ * in the given context.
+ */
+ public static float computeEnforcedRadius(@NonNull Context context) {
+ if (!Utilities.ATLEAST_S) {
+ return 0;
+ }
+ Resources res = context.getResources();
+ float systemRadius = res.getDimension(android.R.dimen.system_app_widget_background_radius);
+ float defaultRadius = res.getDimension(R.dimen.enforced_rounded_corner_max_radius);
+ return Math.min(defaultRadius, systemRadius);
+ }
+
+ private static List<View> findViewsWithId(View view, @IdRes int viewId) {
+ List<View> output = new ArrayList<>();
+ accumulateViewsWithId(view, viewId, output);
+ return output;
+ }
+
+ // Traverse views. If the predicate returns true, continue on the children, otherwise, don't.
+ private static void accumulateViewsWithId(View view, @IdRes int viewId, List<View> output) {
+ if (view.getId() == viewId) {
+ output.add(view);
+ return;
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) view;
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ accumulateViewsWithId(vg.getChildAt(i), viewId, output);
+ }
+ }
+ }
+
+ private static boolean isViewVisible(View view) {
+ if (view.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ return !view.willNotDraw() || view.getForeground() != null || view.getBackground() != null;
+ }
+
+ @Nullable
+ private static View findUndefinedBackground(View current) {
+ if (current.getVisibility() != View.VISIBLE) {
+ return null;
+ }
+ if (isViewVisible(current)) {
+ return current;
+ }
+ View lastVisibleView = null;
+ // Find the first view that is either not a ViewGroup, or a ViewGroup which will draw
+ // something, or a ViewGroup that contains more than one view.
+ if (current instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) current;
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ View visibleView = findUndefinedBackground(vg.getChildAt(i));
+ if (visibleView != null) {
+ if (lastVisibleView != null) {
+ return current; // At least two visible children
+ }
+ lastVisibleView = visibleView;
+ }
+ }
+ }
+ return lastVisibleView;
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 229df50..40b256b 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,10 +18,10 @@
import static com.android.launcher3.Utilities.ATLEAST_S;
-import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import android.os.CancellationSignal;
import android.util.AttributeSet;
import android.util.Log;
@@ -35,13 +35,18 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.RoundDrawableWrapper;
import com.android.launcher3.model.WidgetItem;
/**
@@ -70,6 +75,7 @@
protected int mPreviewHeight;
protected int mPresetPreviewSize;
private int mCellSize;
+ private float mPreviewScale = 1f;
private WidgetImageView mWidgetImage;
private TextView mWidgetName;
@@ -84,14 +90,15 @@
private boolean mAnimatePreview = true;
private boolean mApplyBitmapDeferred = false;
- private Bitmap mDeferredBitmap;
+ private Drawable mDeferredDrawable;
protected final BaseActivity mActivity;
protected final DeviceProfile mDeviceProfile;
private final CheckLongPressHelper mLongPressHelper;
+ private final float mEnforcedCornerRadius;
private RemoteViews mPreview;
- private AppWidgetHostView mPreviewAppWidgetHostView;
+ private LauncherAppWidgetHostView mAppWidgetHostViewPreview;
public WidgetCell(Context context) {
this(context, null);
@@ -113,6 +120,7 @@
setWillNotDraw(false);
setClipToPadding(false);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
+ mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
}
private void setContainerWidth() {
@@ -147,7 +155,7 @@
Log.d(TAG, "reset called on:" + mWidgetName.getText());
}
mWidgetImage.animate().cancel();
- mWidgetImage.setBitmap(null, null);
+ mWidgetImage.setDrawable(null, null);
mWidgetName.setText(null);
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
@@ -159,7 +167,7 @@
mActiveRequest = null;
}
mPreview = null;
- mPreviewAppWidgetHostView = null;
+ mAppWidgetHostViewPreview = null;
}
public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
@@ -194,7 +202,7 @@
&& mPreview == null
&& item.widgetInfo != null
&& item.widgetInfo.previewLayout != Resources.ID_NULL) {
- mPreviewAppWidgetHostView = new AppWidgetHostView(getContext());
+ mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(getContext());
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(),
item.widgetInfo.clone());
@@ -202,11 +210,11 @@
// rendering a preview layout for work profile apps yet. For non-work profile layout, a
// proper solution is to use RemoteViews(PackageName, LayoutId).
launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;
- mPreviewAppWidgetHostView.setAppWidget(/* appWidgetId= */ -1,
+ mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1,
launcherAppWidgetProviderInfo);
- mPreviewAppWidgetHostView.setPadding(/* left= */ 0, /* top= */0, /* right= */
+ mAppWidgetHostViewPreview.setPadding(/* left= */ 0, /* top= */0, /* right= */
0, /* bottom= */ 0);
- mPreviewAppWidgetHostView.updateAppWidget(/* remoteViews= */ null);
+ mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ null);
}
}
@@ -214,6 +222,11 @@
return mWidgetImage;
}
+ @Nullable
+ public LauncherAppWidgetHostView getAppWidgetHostViewPreview() {
+ return mAppWidgetHostViewPreview;
+ }
+
/**
* Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but
* will not cause invalidate, so that when deferring is disabled later, all the bitmaps are
@@ -223,9 +236,9 @@
public void setApplyBitmapDeferred(boolean isDeferred) {
if (mApplyBitmapDeferred != isDeferred) {
mApplyBitmapDeferred = isDeferred;
- if (!mApplyBitmapDeferred && mDeferredBitmap != null) {
- applyPreview(mDeferredBitmap);
- mDeferredBitmap = null;
+ if (!mApplyBitmapDeferred && mDeferredDrawable != null) {
+ applyPreview(mDeferredDrawable);
+ mDeferredDrawable = null;
}
}
}
@@ -235,17 +248,22 @@
}
public void applyPreview(Bitmap bitmap) {
+ FastBitmapDrawable drawable = new FastBitmapDrawable(bitmap);
+ applyPreview(new RoundDrawableWrapper(drawable, mEnforcedCornerRadius));
+ }
+
+ private void applyPreview(Drawable drawable) {
if (mApplyBitmapDeferred) {
- mDeferredBitmap = bitmap;
+ mDeferredDrawable = drawable;
return;
}
- if (bitmap != null) {
+ if (drawable != null) {
LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
- layoutParams.width = bitmap.getWidth();
- layoutParams.height = bitmap.getHeight();
+ layoutParams.width = (int) (drawable.getIntrinsicWidth() * mPreviewScale);
+ layoutParams.height = (int) (drawable.getIntrinsicHeight() * mPreviewScale);
mWidgetImage.setLayoutParams(layoutParams);
- mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
+ mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
if (mAnimatePreview) {
mWidgetImage.setAlpha(0f);
@@ -262,18 +280,26 @@
Bitmap preview = generateFromRemoteViews(
mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
if (preview != null) {
- applyPreview(preview);
+ applyPreview(new FastBitmapDrawable(preview));
return;
}
}
- if (mPreviewAppWidgetHostView != null) {
- Bitmap preview = generateFromView(mActivity, mPreviewAppWidgetHostView,
- mItem.widgetInfo, mPreviewWidth, new int[1]);
- if (preview != null) {
- applyPreview(preview);
- return;
- }
+ if (mAppWidgetHostViewPreview != null) {
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ int viewWidth = dp.cellWidthPx * mItem.spanX;
+ int viewHeight = dp.cellHeightPx * mItem.spanY;
+
+ mAppWidgetHostViewPreview.measure(
+ MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
+
+ viewWidth = mAppWidgetHostViewPreview.getMeasuredWidth();
+ viewHeight = mAppWidgetHostViewPreview.getMeasuredHeight();
+ mAppWidgetHostViewPreview.layout(0, 0, viewWidth, viewHeight);
+ Drawable drawable = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
+ applyPreview(drawable);
+ return;
}
if (mActiveRequest != null) {
return;
@@ -284,10 +310,16 @@
/** Sets the widget preview image size in number of cells. */
public void setPreviewSize(int spanX, int spanY) {
+ setPreviewSize(spanX, spanY, 1f);
+ }
+
+ /** Sets the widget preview image size, in number of cells, and preview scale. */
+ public void setPreviewSize(int spanX, int spanY, float previewScale) {
int padding = 2 * getResources()
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
mPreviewWidth = mDeviceProfile.cellWidthPx * spanX + padding;
mPreviewHeight = mDeviceProfile.cellHeightPx * spanY + padding;
+ mPreviewScale = previewScale;
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index df2bcff..39d701c 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -17,9 +17,7 @@
package com.android.launcher3.widget;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -35,11 +33,10 @@
*/
public class WidgetImageView extends View {
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final RectF mDstRectF = new RectF();
private final int mBadgeMargin;
- private Bitmap mBitmap;
+ private Drawable mDrawable;
private Drawable mBadge;
public WidgetImageView(Context context) {
@@ -57,21 +54,22 @@
.getDimensionPixelSize(R.dimen.profile_badge_margin);
}
- public void setBitmap(Bitmap bitmap, Drawable badge) {
- mBitmap = bitmap;
+ public void setDrawable(Drawable drawable, Drawable badge) {
+ mDrawable = drawable;
mBadge = badge;
invalidate();
}
- public Bitmap getBitmap() {
- return mBitmap;
+ public Drawable getDrawable() {
+ return mDrawable;
}
@Override
protected void onDraw(Canvas canvas) {
- if (mBitmap != null) {
+ if (mDrawable != null) {
updateDstRectF();
- canvas.drawBitmap(mBitmap, null, mDstRectF, mPaint);
+ mDrawable.setBounds(getBitmapBounds());
+ mDrawable.draw(canvas);
// Only draw the badge if a preview was drawn.
if (mBadge != null) {
@@ -91,11 +89,11 @@
private void updateDstRectF() {
float myWidth = getWidth();
float myHeight = getHeight();
- float bitmapWidth = mBitmap.getWidth();
+ float bitmapWidth = mDrawable.getIntrinsicWidth();
final float scale = bitmapWidth > myWidth ? myWidth / bitmapWidth : 1;
float scaledWidth = bitmapWidth * scale;
- float scaledHeight = mBitmap.getHeight() * scale;
+ float scaledHeight = mDrawable.getIntrinsicHeight() * scale;
mDstRectF.left = (myWidth - scaledWidth) / 2;
mDstRectF.right = (myWidth + scaledWidth) / 2;
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index e6d54a9..bbb0d92 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -26,6 +26,7 @@
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
@@ -119,8 +120,7 @@
public void populateAndShow(ItemInfo itemInfo) {
mOriginalItemInfo = itemInfo;
- ((TextView) findViewById(R.id.title)).setText(getContext().getString(
- R.string.widgets_bottom_sheet_title, mOriginalItemInfo.title));
+ ((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);
onWidgetsBound();
attachToContainer();
@@ -140,7 +140,7 @@
WidgetsTableUtils.groupWidgetItemsIntoTable(widgets, mMaxHorizontalSpan).forEach(row -> {
TableRow tableRow = new TableRow(getContext());
- tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ tableRow.setGravity(Gravity.TOP);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
@@ -153,6 +153,19 @@
});
}
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = false;
+ ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
+ if (getPopupContainer().isEventOverView(scrollView, ev)
+ && scrollView.getScrollY() > 0) {
+ mNoIntercept = true;
+ }
+ }
+ return super.onControllerInterceptTouchEvent(ev);
+ }
+
protected WidgetCell addItemCell(ViewGroup parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
.inflate(R.layout.widget_cell, parent, false);
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
new file mode 100644
index 0000000..66bb363
--- /dev/null
+++ b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
@@ -0,0 +1,61 @@
+/*
+ * 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.dragndrop;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+/** A drag listener of {@link LauncherAppWidgetHostView}. */
+public final class AppWidgetHostViewDragListener implements DragController.DragListener {
+ private final Launcher mLauncher;
+ private DropTarget.DragObject mDragObject;
+ private AppWidgetHostViewDrawable mAppWidgetHostViewDrawable;
+ private LauncherAppWidgetHostView mAppWidgetHostView;
+
+ public AppWidgetHostViewDragListener(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions unused) {
+ if (dragObject.dragView.getDrawable() instanceof AppWidgetHostViewDrawable) {
+ mDragObject = dragObject;
+ mAppWidgetHostViewDrawable =
+ (AppWidgetHostViewDrawable) mDragObject.dragView.getDrawable();
+ mAppWidgetHostView = mAppWidgetHostViewDrawable.getAppWidgetHostView();
+ mAppWidgetHostView.startDrag(this);
+ } else {
+ mLauncher.getDragController().removeDragListener(this);
+ }
+ }
+
+ @Override
+ public void onDragEnd() {
+ mAppWidgetHostView.endDrag();
+ mLauncher.getDragController().removeDragListener(this);
+ }
+
+ /** Notifies when there is a content change in the drag view. */
+ public void onDragContentChanged() {
+ if (mDragObject.dragView != null) {
+ mDragObject.dragView.invalidate();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 7eb5b83..7f84077 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -35,6 +35,7 @@
private final SearchAndRecommendationViewHolder mViewHolder;
private final WidgetsRecyclerView mPrimaryRecyclerView;
private final WidgetsRecyclerView mSearchRecyclerView;
+ private final int mTabsHeight;
// The following are only non null if mHasWorkProfile is true.
@Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -42,10 +43,28 @@
@Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
private WidgetsRecyclerView mCurrentRecyclerView;
- private int mMaxCollapsibleHeight = 0;
+
+ /**
+ * The vertical distance, in pixels, until the search is pinned at the top of the screen when
+ * the user scrolls down the recycler view.
+ */
+ private int mCollapsibleHeightForSearch = 0;
+ /**
+ * The vertical distance, in pixels, until the recommendation table disappears from the top of
+ * the screen when the user scrolls down the recycler view.
+ */
+ private int mCollapsibleHeightForRecommendation = 0;
+ /**
+ * The vertical distance, in pixels, until the tabs is pinned at the top of the screen when the
+ * user scrolls down the recycler view.
+ *
+ * <p>Always 0 if there is no work profile.
+ */
+ private int mCollapsibleHeightForTabs = 0;
SearchAndRecommendationsScrollController(
boolean hasWorkProfile,
+ int tabsHeight,
SearchAndRecommendationViewHolder viewHolder,
WidgetsRecyclerView primaryRecyclerView,
@Nullable WidgetsRecyclerView workRecyclerView,
@@ -55,46 +74,63 @@
mHasWorkProfile = hasWorkProfile;
mViewHolder = viewHolder;
mPrimaryRecyclerView = primaryRecyclerView;
+ mCurrentRecyclerView = mPrimaryRecyclerView;
mWorkRecyclerView = workRecyclerView;
mSearchRecyclerView = searchRecyclerView;
mPrimaryWorkTabsView = personalWorkTabsView;
mPrimaryWorkViewPager = primaryWorkViewPager;
mCurrentRecyclerView = mPrimaryRecyclerView;
+ mTabsHeight = tabsHeight;
}
/** Sets the current active {@link WidgetsRecyclerView}. */
public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
mCurrentRecyclerView = currentRecyclerView;
+ mCurrentRecyclerView = currentRecyclerView;
+ mViewHolder.mHeaderTitle.setTranslationY(0);
+ mViewHolder.mRecommendedWidgetsTable.setTranslationY(0);
+ mViewHolder.mSearchBar.setTranslationY(0);
+
+ if (mHasWorkProfile) {
+ mPrimaryWorkTabsView.setTranslationY(0);
+ }
}
/**
* Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+ *
+ * @return {@code true} if margins or/and padding of views in the search and recommendations
+ * container have been updated.
*/
- public void updateMarginAndPadding() {
- // The maximum vertical distance, in pixels, until the last collapsible element is not
- // visible from the screen when the user scrolls down the recycler view.
- mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop()
- + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
- + measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+ public boolean updateMarginAndPadding() {
+ boolean hasMarginOrPaddingUpdated = false;
+ mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+ mCollapsibleHeightForRecommendation =
+ measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+ + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+ + measureHeightWithVerticalMargins((View) mViewHolder.mSearchBar)
+ + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
if (mHasWorkProfile) {
+ mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+ + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
// In a work profile setup, the full widget sheet contains the following views:
- // ------- -|
- // Widgets -|---> LinearLayout for search & recommendations
- // Search bar -|
- // Personal | Work
+ // ------- (pinned) -|
+ // Widgets (collapsible) -|---> LinearLayout for search & recommendations
+ // Search bar (pinned) -|
+ // Widgets recommendation (collapsible)-|
+ // Personal | Work (pinned)
// View Pager
//
// Views after the search & recommendations are not bound by RelativelyLayout param.
// To position them on the expected location, padding & margin are added to these views
// Tabs should have a padding of the height of the search & recommendations container.
- mPrimaryWorkTabsView.setPadding(
- mPrimaryWorkTabsView.getPaddingLeft(),
- topContainerHeight,
- mPrimaryWorkTabsView.getPaddingRight(),
- mPrimaryWorkTabsView.getPaddingBottom());
+ RelativeLayout.LayoutParams tabsLayoutParams =
+ (RelativeLayout.LayoutParams) mPrimaryWorkTabsView.getLayoutParams();
+ tabsLayoutParams.topMargin = topContainerHeight;
+ mPrimaryWorkTabsView.setLayoutParams(tabsLayoutParams);
// Instead of setting the top offset directly, we split the top offset into two values:
// 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
@@ -124,39 +160,52 @@
//
// When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
// mMaxCollapsibleDistance should equal to the top container height.
- int tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView)
- - mPrimaryWorkTabsView.getPaddingTop();
int topOffsetAfterAllViewsCollapsed =
- topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
+ topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
- RelativeLayout.LayoutParams layoutParams =
+ RelativeLayout.LayoutParams viewPagerLayoutParams =
(RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
- layoutParams.setMargins(0, topOffsetAfterAllViewsCollapsed, 0, 0);
- mPrimaryWorkViewPager.setLayoutParams(layoutParams);
- mPrimaryWorkViewPager.requestLayout();
+ if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
+ viewPagerLayoutParams.topMargin = topOffsetAfterAllViewsCollapsed;
+ mPrimaryWorkViewPager.setLayoutParams(viewPagerLayoutParams);
+ hasMarginOrPaddingUpdated = true;
+ }
- mPrimaryRecyclerView.setPadding(
- mPrimaryRecyclerView.getPaddingLeft(),
- mMaxCollapsibleHeight,
- mPrimaryRecyclerView.getPaddingRight(),
- mPrimaryRecyclerView.getPaddingBottom());
- mWorkRecyclerView.setPadding(
- mWorkRecyclerView.getPaddingLeft(),
- mMaxCollapsibleHeight,
- mWorkRecyclerView.getPaddingRight(),
- mWorkRecyclerView.getPaddingBottom());
+ if (mPrimaryRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
+ mPrimaryRecyclerView.setPadding(
+ mPrimaryRecyclerView.getPaddingLeft(),
+ mCollapsibleHeightForTabs,
+ mPrimaryRecyclerView.getPaddingRight(),
+ mPrimaryRecyclerView.getPaddingBottom());
+ hasMarginOrPaddingUpdated = true;
+ }
+ if (mWorkRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
+ mWorkRecyclerView.setPadding(
+ mWorkRecyclerView.getPaddingLeft(),
+ mCollapsibleHeightForTabs,
+ mWorkRecyclerView.getPaddingRight(),
+ mWorkRecyclerView.getPaddingBottom());
+ hasMarginOrPaddingUpdated = true;
+ }
} else {
- mPrimaryRecyclerView.setPadding(
- mPrimaryRecyclerView.getPaddingLeft(),
- topContainerHeight,
- mPrimaryRecyclerView.getPaddingRight(),
- mPrimaryRecyclerView.getPaddingBottom());
+ if (mPrimaryRecyclerView.getPaddingTop() != topContainerHeight) {
+ mPrimaryRecyclerView.setPadding(
+ mPrimaryRecyclerView.getPaddingLeft(),
+ topContainerHeight,
+ mPrimaryRecyclerView.getPaddingRight(),
+ mPrimaryRecyclerView.getPaddingBottom());
+ hasMarginOrPaddingUpdated = true;
+ }
}
- mSearchRecyclerView.setPadding(
- mSearchRecyclerView.getPaddingLeft(),
- topContainerHeight,
- mSearchRecyclerView.getPaddingRight(),
- mSearchRecyclerView.getPaddingBottom());
+ if (mSearchRecyclerView.getPaddingTop() != topContainerHeight) {
+ mSearchRecyclerView.setPadding(
+ mSearchRecyclerView.getPaddingLeft(),
+ topContainerHeight,
+ mSearchRecyclerView.getPaddingRight(),
+ mSearchRecyclerView.getPaddingBottom());
+ hasMarginOrPaddingUpdated = true;
+ }
+ return hasMarginOrPaddingUpdated;
}
/**
@@ -168,13 +217,22 @@
// Always use the recycler view offset because fast scroller offset has a different scale.
int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
if (recyclerViewYOffset < 0) return;
- if (mMaxCollapsibleHeight > 0) {
- int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight);
+
+ if (mCollapsibleHeightForRecommendation > 0) {
+ int yDisplacement = Math.max(-recyclerViewYOffset,
+ -mCollapsibleHeightForRecommendation);
mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
- mViewHolder.mSearchBar.setTranslationY(yDisplacement);
- if (mHasWorkProfile) {
- mPrimaryWorkTabsView.setTranslationY(yDisplacement);
- }
+ mViewHolder.mRecommendedWidgetsTable.setTranslationY(yDisplacement);
+ }
+
+ if (mCollapsibleHeightForSearch > 0) {
+ int searchYDisplacement = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForSearch);
+ mViewHolder.mSearchBar.setTranslationY(searchYDisplacement);
+ }
+
+ if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
+ int yDisplacementForTabs = Math.max(-recyclerViewYOffset, -mCollapsibleHeightForTabs);
+ mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
}
}
@@ -189,6 +247,9 @@
/** private the height, in pixel, + the vertical margins of a given view. */
private static int measureHeightWithVerticalMargins(View view) {
+ if (view.getVisibility() != View.VISIBLE) {
+ return 0;
+ }
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+ marginLayoutParams.topMargin;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 6b3c71a..d13884a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -23,6 +23,7 @@
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
@@ -32,6 +33,8 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -47,6 +50,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
import com.android.launcher3.widget.BaseWidgetSheet;
@@ -54,9 +58,12 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
+import com.android.launcher3.widget.picker.search.WidgetsSearchBarUIHelper;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@@ -65,11 +72,17 @@
*/
public class WidgetsFullSheet extends BaseWidgetSheet
implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
- WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
+ WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener,
+ WidgetsSearchBarUIHelper {
+ private static final String TAG = WidgetsFullSheet.class.getSimpleName();
private static final long DEFAULT_OPEN_DURATION = 267;
private static final long FADE_IN_DURATION = 150;
private static final float VERTICAL_START_POSITION = 0.3f;
+ // The widget recommendation table can easily take over the entire screen on devices with small
+ // resolution or landscape on phone. This ratio defines the max percentage of content area that
+ // the table can display.
+ private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
private final Rect mInsets = new Rect();
private final boolean mHasWorkProfile;
@@ -79,10 +92,12 @@
mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
mPrimaryWidgetsFilter.negate();
+ private final int mTabsHeight;
+ private final int mWidgetCellHorizontalPadding;
@Nullable private PersonalWorkPagedView mViewPager;
- private int mInitialTabsHeight = 0;
private boolean mIsInSearchMode;
+ private int mMaxSpansPerRow = 4;
private View mTabsView;
private TextView mNoWidgetsView;
private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
@@ -94,6 +109,12 @@
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
+ mTabsHeight = mHasWorkProfile
+ ? getContext().getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_header_tab_height)
+ : 0;
+ mWidgetCellHorizontalPadding = 2 * getResources().getDimensionPixelOffset(
+ R.dimen.widget_cell_horizontal_padding);
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -123,21 +144,17 @@
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(1));
fastScroller.setIsRecyclerViewFirstChildInParent(false);
- springLayout.addSpringView(R.id.primary_widgets_list_view);
- springLayout.addSpringView(R.id.work_widgets_list_view);
} else {
mViewPager = null;
- springLayout.addSpringView(R.id.primary_widgets_list_view);
}
layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
true);
- springLayout.addSpringView(R.id.search_and_recommendations_container);
-
mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
findViewById(R.id.search_and_recommendations_container));
mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
mHasWorkProfile,
+ mTabsHeight,
mSearchAndRecommendationViewHolder,
findViewById(R.id.primary_widgets_list_view),
mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
@@ -148,6 +165,7 @@
mNoWidgetsView = findViewById(R.id.no_widgets_text);
+ onRecommendedWidgetsBound();
onWidgetsBound();
mSearchAndRecommendationViewHolder.mSearchBar.initialize(
@@ -161,8 +179,8 @@
mAdapters.get(currentActivePage).mWidgetsRecyclerView;
updateNoWidgetsView(currentAdapterHolder);
-
attachScrollbarToRecyclerView(currentRecyclerView);
+ resetExpandedHeaders();
}
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
@@ -180,6 +198,13 @@
mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
}
+ private void updateNoSearchResultsView(boolean isVisible) {
+ mNoWidgetsView.setVisibility(isVisible ? VISIBLE : GONE);
+ if (isVisible) {
+ mNoWidgetsView.setText(R.string.no_search_results);
+ }
+ }
+
private void reset() {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
if (mHasWorkProfile) {
@@ -224,6 +249,7 @@
mInsets.set(insets);
setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
+ setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom);
if (mHasWorkProfile) {
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
}
@@ -247,6 +273,22 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+ doMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ if (updateMaxSpansPerRow()) {
+ doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+ doMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ }
+
+ private void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
int widthUsed;
if (mInsets.bottom > 0) {
@@ -262,22 +304,29 @@
widthUsed, heightMeasureSpec, heightUsed);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
+ }
- int paddingPx = 2 * getResources().getDimensionPixelOffset(
- R.dimen.widget_cell_horizontal_padding);
- int maxSpansPerRow = getMeasuredWidth() / (deviceProfile.cellWidthPx + paddingPx);
- mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
- maxSpansPerRow);
- if (mHasWorkProfile) {
- mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
- maxSpansPerRow);
+ /** Returns {@code true} if the max spans have been updated. */
+ private boolean updateMaxSpansPerRow() {
+ if (getMeasuredWidth() == 0) return false;
+
+ int previousMaxSpansPerRow = mMaxSpansPerRow;
+ mMaxSpansPerRow = getMeasuredWidth()
+ / (mLauncher.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
+
+ if (previousMaxSpansPerRow != mMaxSpansPerRow) {
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+ mMaxSpansPerRow);
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+ mMaxSpansPerRow);
+ if (mHasWorkProfile) {
+ mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+ mMaxSpansPerRow);
+ }
+ onRecommendedWidgetsBound();
+ return true;
}
-
- if (mInitialTabsHeight == 0 && mTabsView != null) {
- mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
- }
-
- mSearchAndRecommendationsScrollController.updateMarginAndPadding();
+ return false;
}
@Override
@@ -323,24 +372,32 @@
if (mIsInSearchMode) return;
setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
+ resetExpandedHeaders();
}
@Override
public void exitSearchMode() {
+ onSearchResults(new ArrayList<>());
setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
if (mHasWorkProfile) {
mViewPager.snapToPage(AdapterHolder.PRIMARY);
}
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
+
+ mSearchAndRecommendationsScrollController.updateMarginAndPadding();
}
@Override
public void onSearchResults(List<WidgetsListBaseEntry> entries) {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
+ updateNoSearchResultsView(
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.getItemCount() == 0);
}
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
mIsInSearchMode = isInSearchMode;
+ mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
+ .setVisibility(isInSearchMode ? GONE : VISIBLE);
if (mHasWorkProfile) {
mViewPager.setVisibility(isInSearchMode ? GONE : VISIBLE);
mTabsView.setVisibility(isInSearchMode ? GONE : VISIBLE);
@@ -350,6 +407,31 @@
}
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView
.setVisibility(mIsInSearchMode ? VISIBLE : GONE);
+ mNoWidgetsView.setVisibility(GONE);
+ }
+
+ private void resetExpandedHeaders() {
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
+ mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
+ }
+
+ @Override
+ public void onRecommendedWidgetsBound() {
+ List<WidgetItem> recommendedWidgets =
+ mLauncher.getPopupDataProvider().getRecommendedWidgets();
+ WidgetsRecommendationTableLayout table =
+ mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
+ if (recommendedWidgets.size() > 0) {
+ float maxTableHeight =
+ (mLauncher.getDeviceProfile().heightPx - mTabsHeight - getHeaderViewHeight())
+ * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
+ WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
+ mMaxSpansPerRow);
+ table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
+ } else {
+ table.setVisibility(GONE);
+ }
}
private void open(boolean animate) {
@@ -439,7 +521,8 @@
public int getHeaderViewHeight() {
return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
+ measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
- + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mSearchBar);
+ + measureHeightWithVerticalMargins(
+ (View) mSearchAndRecommendationViewHolder.mSearchBar);
}
/** private the height, in pixel, + the vertical margins of a given view. */
@@ -452,6 +535,34 @@
+ marginLayoutParams.topMargin;
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mIsInSearchMode) {
+ mSearchAndRecommendationViewHolder.mSearchBar.reset();
+ }
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ if (mIsInSearchMode) {
+ mSearchAndRecommendationViewHolder.mSearchBar.reset();
+ return true;
+ }
+ return super.onBackPressed();
+ }
+
+ @Override
+ public void onDragStart(boolean start, float startDisplacement) {
+ super.onDragStart(start, startDisplacement);
+ getWindowInsetsController().hide(WindowInsets.Type.ime());
+ }
+
+ @Override
+ public void clearSearchBarFocus() {
+ mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
+ }
+
/** A holder class for holding adapters & their corresponding recycler view. */
private final class AdapterHolder {
static final int PRIMARY = 0;
@@ -474,7 +585,10 @@
apps.getWidgetCache(),
apps.getIconCache(),
/* iconClickListener= */ WidgetsFullSheet.this,
- /* iconLongClickListener= */ WidgetsFullSheet.this);
+ /* iconLongClickListener= */ WidgetsFullSheet.this,
+ /* WidgetsSearchBarUIHelper= */
+ mAdapterType == SEARCH ? WidgetsFullSheet.this : null);
+ mWidgetsListAdapter.setHasStableIds(true);
switch (mAdapterType) {
case PRIMARY:
mWidgetsListAdapter.setFilter(mPrimaryWidgetsFilter);
@@ -490,24 +604,35 @@
void setup(WidgetsRecyclerView recyclerView) {
mWidgetsRecyclerView = recyclerView;
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
+ // Disables animation because it disrupts the item focus upon adapter item change.
+ mWidgetsRecyclerView.setItemAnimator(null);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
mWidgetsRecyclerView.setEdgeEffectFactory(
((TopRoundedCornerView) mContent).createEdgeEffectFactory());
mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
+ mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
}
}
final class SearchAndRecommendationViewHolder {
- final View mContainer;
+ final ViewGroup mContainer;
final View mCollapseHandle;
final WidgetsSearchBar mSearchBar;
final TextView mHeaderTitle;
+ final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
- SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
+ SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) {
mContainer = searchAndRecommendationContainer;
mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
mHeaderTitle = mContainer.findViewById(R.id.title);
+ mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
+ mRecommendedWidgetsTable.setWidgetCellOnTouchListener((view, event) -> {
+ getRecyclerView().onTouchEvent(event);
+ return false;
+ });
+ mRecommendedWidgetsTable.setWidgetCellLongClickListener(WidgetsFullSheet.this);
+ mRecommendedWidgetsTable.setWidgetCellOnClickListener(WidgetsFullSheet.this);
}
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 9009eb1..d9c9d4d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
import android.content.Context;
+import android.os.Process;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -40,8 +41,10 @@
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
+import com.android.launcher3.widget.picker.search.WidgetsSearchBarUIHelper;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
@@ -67,6 +70,7 @@
private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
+ @Nullable private final WidgetsSearchBarUIHelper mSearchBarUIHelper;
private final WidgetsDiffReporter mDiffReporter;
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
@@ -81,24 +85,27 @@
entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry
|| new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
- .equals(mWidgetsContentVisiblePackageUserKey);
+ .equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
- OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
+ OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
+ @Nullable WidgetsSearchBarUIHelper searchBarUIHelper) {
+ mSearchBarUIHelper = searchBarUIHelper;
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
- layoutInflater, iconClickListener, iconLongClickListener, widgetPreviewLoader);
+ layoutInflater, iconClickListener, iconLongClickListener,
+ widgetPreviewLoader, /* listAdapter= */ this);
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
- layoutInflater, /*onHeaderClickListener=*/this));
+ layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SEARCH_HEADER,
new WidgetsListSearchHeaderViewHolderBinder(
- layoutInflater, /*onHeaderClickListener=*/ this));
+ layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this));
}
public void setFilter(Predicate<WidgetsListBaseEntry> filter) {
@@ -175,10 +182,18 @@
mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
}
+ /**
+ * Resets any expanded widget header.
+ */
+ public void resetExpandedHeader() {
+ mWidgetsContentVisiblePackageUserKey = null;
+ updateVisibleEntries();
+ }
+
@Override
public void onBindViewHolder(ViewHolder holder, int pos) {
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
- viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos));
+ viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
}
@Override
@@ -206,7 +221,9 @@
@Override
public long getItemId(int pos) {
- return pos;
+ return Arrays.hashCode(new Object[]{
+ mVisibleEntries.get(pos).mPkgItem.hashCode(),
+ getItemViewType(pos)});
}
@Override
@@ -224,6 +241,9 @@
@Override
public void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey) {
+ if (mSearchBarUIHelper != null) {
+ mSearchBarUIHelper.clearSearchBarFocus();
+ }
if (showWidgets) {
mWidgetsContentVisiblePackageUserKey = packageUserKey;
updateVisibleEntries();
@@ -258,7 +278,14 @@
@Override
public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
- return mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+ int i = mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+ if (i != 0) {
+ return i;
+ }
+ // Prioritize entries from current user over other users if the entries are same.
+ if (a.mPkgItem.user.equals(b.mPkgItem.user)) return 0;
+ if (a.mPkgItem.user.equals(Process.myUserHandle())) return -1;
+ return 1;
}
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 119d094..497c72e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
-
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -29,13 +27,14 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.graphics.PlaceHolderIconDrawable;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.PlaceHolderIconDrawable;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -58,6 +57,7 @@
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
private final int mIconSize;
+ private final int mBottomMarginSize;
private ImageView mAppIcon;
private TextView mTitle;
@@ -83,6 +83,8 @@
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
+ mBottomMarginSize =
+ getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
}
@Override
@@ -113,6 +115,13 @@
public void setExpanded(boolean isExpanded) {
this.mIsExpanded = isExpanded;
mExpandToggle.setChecked(isExpanded);
+ if (getLayoutParams() instanceof RecyclerView.LayoutParams) {
+ int bottomMargin = isExpanded ? 0 : mBottomMarginSize;
+ RecyclerView.LayoutParams layoutParams =
+ ((RecyclerView.LayoutParams) getLayoutParams());
+ layoutParams.bottomMargin = bottomMargin;
+ setLayoutParams(layoutParams);
+ }
}
/** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
@@ -134,7 +143,7 @@
}
private void setIcon(PackageItemInfo info) {
- FastBitmapDrawable icon = newIcon(getContext(), info);
+ FastBitmapDrawable icon = info.newIcon(getContext());
applyDrawables(icon);
mIconDrawable = icon;
if (mIconDrawable != null) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index fcefe3a..e57f4d8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -30,11 +30,14 @@
ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener;
+ private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
- OnHeaderClickListener onHeaderClickListener) {
+ OnHeaderClickListener onHeaderClickListener,
+ WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener;
+ mWidgetsListAdapter = listAdapter;
}
@Override
@@ -46,8 +49,18 @@
}
@Override
- public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data) {
+ public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
+ int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+ if (mWidgetsListAdapter.getItemCount() == 1) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_single_item_ripple);
+ } else if (position == 0) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
+ } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+ } else {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+ }
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index 83c7948..b98f5e1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -31,11 +31,14 @@
ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener;
+ private final WidgetsListAdapter mWidgetsListAdapter;
public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
- OnHeaderClickListener onHeaderClickListener) {
+ OnHeaderClickListener onHeaderClickListener,
+ WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener;
+ mWidgetsListAdapter = listAdapter;
}
@Override
@@ -48,8 +51,17 @@
@Override
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
- WidgetsListSearchHeaderEntry data) {
+ WidgetsListSearchHeaderEntry data, int position) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+ if (mWidgetsListAdapter.getItemCount() == 1) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_single_item_ripple);
+ } else if (position == 0) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
+ } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+ } else {
+ widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+ }
widgetsListHeader.applyFromItemInfoWithIcon(data);
widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 47fa71a..c1d64b1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -52,6 +52,7 @@
private final OnClickListener mIconClickListener;
private final OnLongClickListener mIconLongClickListener;
private final WidgetPreviewLoader mWidgetPreviewLoader;
+ private final WidgetsListAdapter mWidgetsListAdapter;
private boolean mApplyBitmapDeferred = false;
public WidgetsListTableViewHolderBinder(
@@ -59,12 +60,14 @@
LayoutInflater layoutInflater,
OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener,
- WidgetPreviewLoader widgetPreviewLoader) {
+ WidgetPreviewLoader widgetPreviewLoader,
+ WidgetsListAdapter listAdapter) {
mLayoutInflater = layoutInflater;
mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
mWidgetPreviewLoader = widgetPreviewLoader;
+ mWidgetsListAdapter = listAdapter;
}
/**
@@ -97,13 +100,22 @@
}
@Override
- public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry) {
+ public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
+ int position) {
TableLayout table = holder.mTableContainer;
if (DEBUG) {
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
entry.mWidgets.size(), table.getChildCount()));
}
+ if (position == mWidgetsListAdapter.getItemCount() - 1) {
+ table.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+ } else {
+ // WidgetsListContentEntry is never shown in position 0. There must be a header above
+ // it.
+ table.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+ }
+
List<ArrayList<WidgetItem>> widgetItemsTable =
WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
recycleTableBeforeBinding(table, widgetItemsTable);
@@ -146,7 +158,7 @@
tableRow = (TableRow) table.getChildAt(i);
} else {
tableRow = new TableRow(table.getContext());
- tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
if (tableRow.getChildCount() > widgetItems.size()) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
new file mode 100644
index 0000000..6569fb0
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -0,0 +1,175 @@
+/*
+ * 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.Gravity;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A {@link TableLayout} for showing recommended widgets. */
+public final class WidgetsRecommendationTableLayout extends TableLayout {
+ private static final float SCALE_DOWN_RATIO = 0.9f;
+ private final DeviceProfile mDeviceProfile;
+ private final float mWidgetCellTextViewsHeight;
+
+ private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
+ @Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
+ @Nullable private OnClickListener mWidgetCellOnClickListener;
+ @Nullable private OnTouchListener mWidgetCellOnTouchListener;
+
+ public WidgetsRecommendationTableLayout(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public WidgetsRecommendationTableLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDeviceProfile = Launcher.getLauncher(context).getDeviceProfile();
+ // There are 1 row for title, 1 row for dimension and 2 rows for description.
+ mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
+ }
+
+ /** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
+ public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
+ mWidgetCellOnLongClickListener = onLongClickListener;
+ }
+
+ /** Sets a {@link android.view.View.OnClickListener} for all widget cells in this table. */
+ public void setWidgetCellOnClickListener(OnClickListener widgetCellOnClickListener) {
+ mWidgetCellOnClickListener = widgetCellOnClickListener;
+ }
+
+ /** Sets a {@link android.view.View.OnTouchListener} for all widget cells in this table. */
+ public void setWidgetCellOnTouchListener(OnTouchListener widgetCellOnTouchListener) {
+ mWidgetCellOnTouchListener = widgetCellOnTouchListener;
+ }
+
+ /**
+ * Sets a list of recommended widgets that would like to be displayed in this table within the
+ * desired {@code recommendationTableMaxHeight}.
+ *
+ * <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
+ * last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
+ * row still doesn't fit, we scale down the preview image.
+ */
+ public void setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+ float recommendationTableMaxHeight) {
+ mRecommendationTableMaxHeight = recommendationTableMaxHeight;
+ RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
+ recommendedWidgets);
+ bindData(data);
+ }
+
+ private void bindData(RecommendationTableData data) {
+ if (data.mRecommendationTable.size() == 0) {
+ setVisibility(GONE);
+ return;
+ }
+
+ removeAllViews();
+
+ for (int i = 0; i < data.mRecommendationTable.size(); i++) {
+ List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
+ TableRow tableRow = new TableRow(getContext());
+ tableRow.setGravity(Gravity.TOP);
+
+ for (WidgetItem widgetItem : widgetItems) {
+ WidgetCell widgetCell = addItemCell(tableRow);
+ widgetCell.setPreviewSize(widgetItem.spanX, widgetItem.spanY, data.mPreviewScale);
+ widgetCell.applyFromCellItem(widgetItem,
+ LauncherAppState.getInstance(getContext()).getWidgetCache());
+ widgetCell.ensurePreview();
+ }
+ addView(tableRow);
+ }
+ setVisibility(VISIBLE);
+ }
+
+ private WidgetCell addItemCell(ViewGroup parent) {
+ WidgetCell widget = (WidgetCell) LayoutInflater.from(
+ getContext()).inflate(R.layout.widget_cell, parent, false);
+
+ widget.setOnTouchListener(mWidgetCellOnTouchListener);
+ WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+ preview.setOnClickListener(mWidgetCellOnClickListener);
+ preview.setOnLongClickListener(mWidgetCellOnLongClickListener);
+ widget.setAnimatePreview(false);
+
+ parent.addView(widget);
+ return widget;
+ }
+
+ private RecommendationTableData fitRecommendedWidgetsToTableSpace(
+ float previewScale,
+ List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
+ // A naive estimation of the widgets recommendation table height without inflation.
+ float totalHeight = 0;
+ for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
+ List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
+ float rowHeight = 0;
+ for (int j = 0; j < widgetItems.size(); j++) {
+ float previewHeight = widgetItems.get(j).spanY * mDeviceProfile.allAppsCellHeightPx
+ * previewScale;
+ rowHeight = Math.max(rowHeight, previewHeight + mWidgetCellTextViewsHeight);
+ }
+ totalHeight += rowHeight;
+ }
+
+ if (totalHeight < mRecommendationTableMaxHeight) {
+ return new RecommendationTableData(recommendedWidgetsInTable, previewScale);
+ }
+
+ if (recommendedWidgetsInTable.size() > 1) {
+ // We don't want to scale down widgets preview unless we really need to. Reduce the
+ // num of row by 1 to see if it fits.
+ return fitRecommendedWidgetsToTableSpace(
+ previewScale,
+ recommendedWidgetsInTable.subList(/* fromIndex= */0,
+ /* toIndex= */recommendedWidgetsInTable.size() - 1));
+ }
+
+ float nextPreviewScale = previewScale * SCALE_DOWN_RATIO;
+ return fitRecommendedWidgetsToTableSpace(nextPreviewScale, recommendedWidgetsInTable);
+ }
+
+ /** Data class for the widgets recommendation table and widgets preview scaling. */
+ private class RecommendationTableData {
+ private final List<ArrayList<WidgetItem>> mRecommendationTable;
+ private final float mPreviewScale;
+
+ RecommendationTableData(List<ArrayList<WidgetItem>> recommendationTable,
+ float previewScale) {
+ mRecommendationTable = recommendationTable;
+ mPreviewScale = previewScale;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 9ab6424..b016b4f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -34,6 +34,7 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
/**
* The widgets recycler view.
@@ -219,7 +220,8 @@
int totalItemsHeight = 0;
for (int i = 0; i < untilIndex; i++) {
WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
- if (entry instanceof WidgetsListHeaderEntry) {
+ if (entry instanceof WidgetsListHeaderEntry
+ || entry instanceof WidgetsListSearchHeaderEntry) {
totalItemsHeight += mEstimatedWidgetListHeaderHeight;
} else if (entry instanceof WidgetsListContentEntry) {
totalItemsHeight += mLastVisibleWidgetContentTableHeight;
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
new file mode 100644
index 0000000..56a08b1
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -0,0 +1,87 @@
+/*
+ * 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.search;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.R;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.util.List;
+
+/**
+ * View for a search bar with an edit text with a cancel button.
+ */
+public class LauncherWidgetsSearchBar extends LinearLayout implements WidgetsSearchBar {
+ private WidgetsSearchBarController mController;
+ private ExtendedEditText mEditText;
+ private ImageButton mCancelButton;
+
+ public LauncherWidgetsSearchBar(Context context) {
+ this(context, null, 0);
+ }
+
+ public LauncherWidgetsSearchBar(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LauncherWidgetsSearchBar(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void initialize(List<WidgetsListBaseEntry> allWidgets,
+ SearchModeListener searchModeListener) {
+ SearchAlgorithm<WidgetsListBaseEntry> algo =
+ new SimpleWidgetsSearchAlgorithm(new SimpleWidgetsSearchPipeline(allWidgets));
+ mController = new WidgetsSearchBarController(
+ algo, mEditText, mCancelButton, searchModeListener);
+ }
+
+ @Override
+ public void reset() {
+ mController.clearSearchResult();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mEditText = findViewById(R.id.widgets_search_bar_edit_text);
+ mCancelButton = findViewById(R.id.widgets_search_cancel_button);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mController.onDestroy();
+ }
+
+ @Override
+ public void clearSearchBarFocus() {
+ mController.clearFocus();
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
index d8e9733..3ac82c0 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -16,64 +16,31 @@
package com.android.launcher3.widget.picker.search;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.util.List;
/**
- * View for a search bar with an edit text with a cancel button.
+ * Interface for a widgets picker search bar.
*/
-public class WidgetsSearchBar extends LinearLayout {
- private WidgetsSearchBarController mController;
- private EditText mEditText;
- private ImageButton mCancelButton;
-
- public WidgetsSearchBar(Context context) {
- this(context, null, 0);
- }
-
- public WidgetsSearchBar(@NonNull Context context,
- @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public WidgetsSearchBar(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
+public interface WidgetsSearchBar {
/**
* Attaches a controller to the search bar which interacts with {@code searchModeListener}.
*/
- public void initialize(List<WidgetsListBaseEntry> allWidgets,
- SearchModeListener searchModeListener) {
- SearchAlgorithm<WidgetsListBaseEntry> algo =
- new SimpleWidgetsSearchAlgorithm(new SimpleWidgetsSearchPipeline(allWidgets));
- mController = new WidgetsSearchBarController(
- algo, mEditText, mCancelButton, searchModeListener);
- }
+ void initialize(List<WidgetsListBaseEntry> allWidgets, SearchModeListener searchModeListener);
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mEditText = findViewById(R.id.widgets_search_bar_edit_text);
- mCancelButton = findViewById(R.id.widgets_search_cancel_button);
- }
+ /**
+ * Clears search bar.
+ */
+ void reset();
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mController.onDestroy();
- }
+ /**
+ * Clears focus from search bar.
+ */
+ void clearSearchBarFocus();
+
+ /**
+ * Sets the vertical location, in pixels, of this search bar relative to its top position.
+ */
+ void setTranslationY(float translationY);
}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index 6c37484..d35a75b 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -22,9 +22,11 @@
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
-import android.widget.EditText;
+import android.view.KeyEvent;
+import android.view.View;
import android.widget.ImageButton;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -35,22 +37,25 @@
* Controller for a search bar with an edit text and a cancel button.
*/
public class WidgetsSearchBarController implements TextWatcher,
- SearchCallback<WidgetsListBaseEntry> {
+ SearchCallback<WidgetsListBaseEntry>, ExtendedEditText.OnBackKeyListener,
+ View.OnKeyListener {
private static final String TAG = "WidgetsSearchBarController";
private static final boolean DEBUG = false;
protected SearchAlgorithm<WidgetsListBaseEntry> mSearchAlgorithm;
- protected EditText mInput;
+ protected ExtendedEditText mInput;
protected ImageButton mCancelButton;
protected SearchModeListener mSearchModeListener;
protected String mQuery;
public WidgetsSearchBarController(
- SearchAlgorithm<WidgetsListBaseEntry> algo, EditText editText, ImageButton cancelButton,
- SearchModeListener searchModeListener) {
+ SearchAlgorithm<WidgetsListBaseEntry> algo, ExtendedEditText editText,
+ ImageButton cancelButton, SearchModeListener searchModeListener) {
mSearchAlgorithm = algo;
mInput = editText;
mInput.addTextChangedListener(this);
+ mInput.setOnBackKeyListener(this);
+ mInput.setOnKeyListener(this);
mCancelButton = cancelButton;
mCancelButton.setOnClickListener(v -> clearSearchResult());
mSearchModeListener = searchModeListener;
@@ -98,7 +103,7 @@
public void clearSearchResult() {
mSearchAlgorithm.cancel(/* interruptActiveRequests= */ true);
mInput.getText().clear();
- mInput.clearFocus();
+ clearFocus();
mSearchModeListener.exitSearchMode();
}
@@ -108,4 +113,27 @@
public void onDestroy() {
mSearchAlgorithm.destroy();
}
+
+ @Override
+ public boolean onBackKey() {
+ clearFocus();
+ return true;
+ }
+
+ @Override
+ public boolean onKey(View view, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+ clearFocus();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clears focus from edit text.
+ */
+ public void clearFocus() {
+ mInput.clearFocus();
+ mInput.hideKeyboard();
+ }
}
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java
similarity index 66%
copy from src/com/android/launcher3/util/SafeCloseable.java
copy to src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java
index ba8ee04..edfdc65 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarUIHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.launcher3.widget.picker.search;
/**
- * Extension of closeable which does not throw an exception
+ * UI helper for {@link WidgetsSearchBar}.
*/
-public interface SafeCloseable extends AutoCloseable {
-
- @Override
- void close();
+public interface WidgetsSearchBarUIHelper {
+ /**
+ * Clears focus from the search bar.
+ */
+ void clearSearchBarFocus();
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 3ea4766..f82f2cc 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -132,7 +132,7 @@
widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
updatedItems.add(info);
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, app);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -149,7 +149,7 @@
}
private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- LauncherAppState app) {
+ LauncherAppState app, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
@@ -158,8 +158,12 @@
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
HashMap<PackageUserKey, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
- // clear the lists.
- mWidgetsList.clear();
+ // Clear the lists only if this is an update on all widgets and shortcuts. If packageUser
+ // isn't null, only updates the shortcuts and widgets for the app represented in
+ // packageUser.
+ if (packageUser == null) {
+ mWidgetsList.clear();
+ }
// add and update.
mWidgetsList.putAll(rawWidgetsShortcuts.stream()
.filter(new WidgetValidityCheck(app))
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index a4e53a1..ff28148 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -56,7 +56,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return ALL_APPS_HEADER | ALL_APPS_CONTENT;
+ return ALL_APPS_CONTENT;
}
@Override
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..da55c28
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "packages_apps_Launcher3_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["packages_apps_Launcher3_license"],
+}
+
+filegroup {
+ name: "launcher3-test-src-common",
+ srcs: ["src_common/**/*.java"],
+}
diff --git a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
index c5ab030..21625c6 100644
--- a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
+++ b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
@@ -18,7 +18,7 @@
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
- android:background="@android:color/system_primary_500"/>
+ android:background="@android:color/system_neutral1_500"/>
</LinearLayout>
<LinearLayout
android:orientation = "horizontal"
@@ -32,7 +32,7 @@
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
- android:background="@android:color/system_secondary_500"/>
+ android:background="@android:color/system_accent1_500"/>
</LinearLayout>
<LinearLayout
android:orientation = "horizontal"
@@ -46,7 +46,7 @@
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
- android:background="@android:color/system_neutral_500"/>
+ android:background="@android:color/system_neutral2_500"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 0edfbed..d4e8f1f 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -503,6 +503,7 @@
// Destroy Launcher activity.
executeOnLauncher(launcher -> {
if (launcher != null) {
+ onLauncherActivityClose(launcher);
launcher.finish();
}
});
@@ -511,7 +512,7 @@
}
protected boolean isInBackground(Launcher launcher) {
- return !launcher.hasBeenResumed();
+ return launcher == null || !launcher.hasBeenResumed();
}
protected boolean isInState(Supplier<LauncherState> state) {
@@ -524,7 +525,7 @@
return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
}
- private static void checkLauncherIntegrity(
+ private void checkLauncherIntegrity(
Launcher launcher, ContainerType expectedContainerType) {
if (launcher != null) {
final StateManager<LauncherState> stateManager = launcher.getStateManager();
@@ -535,10 +536,8 @@
stableState == stateManager.getState());
final boolean isResumed = launcher.hasBeenResumed();
- assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
- isResumed == launcher.isStarted());
- assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
- isResumed == launcher.isUserActive());
+ final boolean isStarted = launcher.isStarted();
+ checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
final int ordinal = stableState.ordinal;
@@ -561,8 +560,7 @@
break;
}
case OVERVIEW: {
- assertTrue(
- "Launcher is not resumed in state: " + expectedContainerType,
+ checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
isResumed);
assertTrue(TestProtocol.stateOrdinalToString(ordinal),
ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
@@ -587,4 +585,20 @@
expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
}
}
+
+ protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
+ boolean isResumed, boolean isStarted) {
+ assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
+ isResumed == isStarted);
+ assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
+ isResumed == launcher.isUserActive());
+ }
+
+ protected void checkLauncherStateInOverview(Launcher launcher,
+ ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
+ assertTrue("Launcher is not resumed in state: " + expectedContainerType,
+ isResumed);
+ }
+
+ protected void onLauncherActivityClose(Launcher launcher) { }
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4c9a8e7..6f47df0 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -334,20 +334,31 @@
// 1. Open all apps and wait for load complete.
// 2. Find the app and long press it to show shortcuts.
// 3. Press icon center until shortcuts appear
- final AllApps allApps = mLauncher.
- getWorkspace().
- switchToAllApps();
+ final AllApps allApps = mLauncher
+ .getWorkspace()
+ .switchToAllApps();
allApps.freeze();
try {
- final AppIconMenuItem menuItem = allApps.
- getAppIcon(APP_NAME).
- openMenu().
- getMenuItem(0);
- final String shortcutName = menuItem.getText();
- assertEquals("Wrong menu item", "Shortcut 3", shortcutName);
+ final AppIconMenu menu = allApps
+ .getAppIcon(APP_NAME)
+ .openMenu();
+ final AppIconMenuItem menuItem0 = menu.getMenuItem(0);
+ final AppIconMenuItem menuItem2 = menu.getMenuItem(2);
+
+ final AppIconMenuItem menuItem;
+
+ final String expectedShortcutName = "Shortcut 3";
+ if (menuItem0.getText().equals(expectedShortcutName)) {
+ menuItem = menuItem0;
+ } else {
+ final String shortcutName2 = menuItem2.getText();
+ assertEquals("Wrong menu item", expectedShortcutName, shortcutName2);
+ menuItem = menuItem2;
+ }
menuItem.dragToWorkspace(false, false);
- mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
+ mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
+ .launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 8f4381b..bfacc74 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -42,6 +42,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,6 +91,7 @@
});
}
+ @Ignore("b/182844465")
@Test
public void workTabExists() {
mDevice.pressHome();
@@ -102,6 +104,7 @@
launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
}
+ @Ignore("b/182844465")
@Test
public void toggleWorks() {
mDevice.pressHome();
@@ -133,6 +136,7 @@
l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
}
+ @Ignore("b/182844465")
@Test
public void testWorkEduFlow() {
mDevice.pressHome();
@@ -176,6 +180,7 @@
});
}
+ @Ignore("b/182844465")
@Test
public void testWorkEduIntermittent() {
mDevice.pressHome();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5138f02..475a4ff 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -52,6 +52,7 @@
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
@@ -585,11 +586,7 @@
"but the current state is not " + containerType.name())) {
switch (containerType) {
case WORKSPACE: {
- if (mDevice.isNaturalOrientation()) {
- waitForLauncherObject(APPS_RES_ID);
- } else {
- waitUntilLauncherObjectGone(APPS_RES_ID);
- }
+ waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
return waitForLauncherObject(WORKSPACE_RES_ID);
@@ -871,6 +868,16 @@
return object;
}
+ @Nullable
+ UiObject2 findObjectInContainer(UiObject2 container, BySelector selector) {
+ try {
+ return container.findObject(selector);
+ } catch (StaleObjectException e) {
+ fail("The container disappeared from screen");
+ return null;
+ }
+ }
+
@NonNull
List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
try {
@@ -1063,6 +1070,11 @@
final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
+ scrollDownByDistance(container, distance);
+ }
+
+ void scrollDownByDistance(UiObject2 container, int distance) {
+ final Rect containerRect = getVisibleBounds(container);
final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
scroll(
container,
@@ -1218,7 +1230,7 @@
final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
assertTrue("injectInputEvent failed",
- mInstrumentation.getUiAutomation().injectInputEvent(event, true));
+ mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
event.recycle();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 22f4d31..a3f9ade 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -17,8 +17,8 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+import static com.android.launcher3.tapl.LauncherInstrumentation.log;
-import android.graphics.Point;
import android.graphics.Rect;
import androidx.test.uiautomator.By;
@@ -30,13 +30,13 @@
import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
-import java.util.List;
/**
* All widgets container.
*/
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
private static final int FLING_STEPS = 10;
+ private static final int SCROLL_ATTEMPTS = 60;
Widgets(LauncherInstrumentation launcher) {
super(launcher);
@@ -50,7 +50,7 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to fling forward in widgets")) {
- LauncherInstrumentation.log("Widgets.flingForward enter");
+ log("Widgets.flingForward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
mLauncher.scroll(
widgetsContainer,
@@ -61,7 +61,7 @@
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
verifyActiveContainer();
}
- LauncherInstrumentation.log("Widgets.flingForward exit");
+ log("Widgets.flingForward exit");
}
}
@@ -72,7 +72,7 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to fling backwards in widgets")) {
- LauncherInstrumentation.log("Widgets.flingBackward enter");
+ log("Widgets.flingBackward enter");
final UiObject2 widgetsContainer = verifyActiveContainer();
mLauncher.scroll(
widgetsContainer,
@@ -82,7 +82,7 @@
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
}
- LauncherInstrumentation.log("Widgets.flingBackward exit");
+ log("Widgets.flingBackward exit");
}
}
@@ -101,23 +101,27 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting widget " + labelText + " in widgets list")) {
+ final UiObject2 searchBar = findSearchBar();
+ final int searchBarHeight = searchBar.getVisibleBounds().height();
final UiObject2 fullWidgetsPicker = verifyActiveContainer();
mLauncher.assertTrue("Widgets container didn't become scrollable",
fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
- final Point displaySize = mLauncher.getRealDisplaySize();
final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer();
mLauncher.assertTrue("Can't locate widgets list for the test app: "
+ mLauncher.getLauncherPackageName(),
widgetsContainer != null);
final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
+ final BySelector previewSelector = By.res(mLauncher.getLauncherPackageName(),
+ "widget_preview");
int i = 0;
for (; ; ) {
final Collection<UiObject2> tableRows = widgetsContainer.getChildren();
for (UiObject2 row : tableRows) {
final Collection<UiObject2> widgetCells = row.getChildren();
for (UiObject2 widget : widgetCells) {
- final UiObject2 label = widget.findObject(labelSelector);
+ final UiObject2 label = mLauncher.findObjectInContainer(widget,
+ labelSelector);
if (label == null) {
continue;
}
@@ -125,14 +129,15 @@
"View is not WidgetCell",
"com.android.launcher3.widget.WidgetCell",
widget.getClassName());
-
- return new Widget(mLauncher, widget);
+ UiObject2 preview = mLauncher.waitForObjectInContainer(widget,
+ previewSelector);
+ return new Widget(mLauncher, preview);
}
}
- mLauncher.assertTrue("Too many attempts", ++i <= 40);
+ mLauncher.assertTrue("Too many attempts", ++i <= SCROLL_ATTEMPTS);
final int scroll = getWidgetsScroll();
- mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, tableRows, 0);
+ mLauncher.scrollDownByDistance(fullWidgetsPicker, searchBarHeight);
final int newScroll = getWidgetsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in Widgets: from " + scroll + " to "
@@ -142,6 +147,18 @@
}
}
+ private UiObject2 findSearchBar() {
+ final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(),
+ "search_and_recommendations_container");
+ final BySelector searchBarSelector = By.res(mLauncher.getLauncherPackageName(),
+ "widgets_search_bar");
+ final UiObject2 searchBarContainer = mLauncher.waitForLauncherObject(
+ searchBarContainerSelector);
+ UiObject2 searchBar = mLauncher.waitForObjectInContainer(searchBarContainer,
+ searchBarSelector);
+ return searchBar;
+ }
+
/** Finds the widgets list of this test app from the collapsed full widgets picker. */
private UiObject2 findTestAppWidgetsTableContainer() {
final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
@@ -152,38 +169,34 @@
"widgets_table");
boolean hasHeaderExpanded = false;
- for (int i = 0; i < 40; i++) {
+ for (int i = 0; i < SCROLL_ATTEMPTS; i++) {
UiObject2 fullWidgetsPicker = verifyActiveContainer();
- UiObject2 header = fullWidgetsPicker.findObject(headerSelector);
- mLauncher.assertTrue("Can't find a widget header", header != null);
+ UiObject2 header = mLauncher.waitForObjectInContainer(fullWidgetsPicker,
+ headerSelector);
+ int headerHeight = header.getVisibleBounds().height();
// Look for a header that has the test app name.
- UiObject2 headerTitle = fullWidgetsPicker.findObject(targetAppSelector);
+ UiObject2 headerTitle = mLauncher.findObjectInContainer(fullWidgetsPicker,
+ targetAppSelector);
if (headerTitle != null) {
// If we find the header and it has not been expanded, let's click it to see the
// widgets list.
if (!hasHeaderExpanded) {
+ log("Header has not been expanded. Click to expand.");
hasHeaderExpanded = true;
mLauncher.clickLauncherObject(headerTitle);
- // After clicking the header, the recyclerview has been updated. Let's refresh
- // the container UIObject2.
- fullWidgetsPicker = verifyActiveContainer();
- // Refresh headerTitle because the first instance is stale after
- // verifyActiveContainer call.
- headerTitle = fullWidgetsPicker.findObject(targetAppSelector);
}
// Look for a widgets list.
- UiObject2 widgetsContainer = fullWidgetsPicker.findObject(widgetsContainerSelector);
+ UiObject2 widgetsContainer = mLauncher.findObjectInContainer(fullWidgetsPicker,
+ widgetsContainerSelector);
if (widgetsContainer != null) {
+ log("Widgets container found.");
return widgetsContainer;
}
- mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, List.of(headerTitle), 0);
- } else {
- mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, fullWidgetsPicker.getChildren(),
- 0);
}
+ mLauncher.scrollDownByDistance(fullWidgetsPicker, headerHeight);
}
return null;