Have All Set aniamted background extend edge-to-edge am: 96431570d4
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19429240
Change-Id: Ideffeaa3d083feaa0a3d8e110d25c0b1eb5c3153
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 0a55675..4adbf53 100644
--- a/Android.bp
+++ b/Android.bp
@@ -313,3 +313,138 @@
baseline_filename: "lint-baseline-launcher3.xml",
},
}
+
+// Build rule for Launcher3 Go app for Android Go devices.
+android_app {
+ name: "Launcher3Go",
+
+ static_libs: ["Launcher3CommonDepsLib"],
+
+ srcs: [
+ "src/**/*.java",
+ "src_ui_overrides/**/*.java",
+ "go/src/**/*.java",
+ ],
+
+ resource_dirs: ["go/res"],
+
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+
+ sdk_version: "current",
+ min_sdk_version: "current",
+ target_sdk_version: "current",
+ privileged: true,
+ system_ext_specific: true,
+ overrides: [
+ "Home",
+ "Launcher2",
+ "Launcher3",
+ "Launcher3QuickStep",
+ ],
+ required: ["privapp_whitelist_com.android.launcher3"],
+
+ additional_manifests: [
+ "AndroidManifest.xml",
+ "AndroidManifest-common.xml",
+ ],
+
+ manifest: "go/AndroidManifest.xml",
+ jacoco: {
+ include_filter: ["com.android.launcher3.*"],
+ }
+
+}
+
+// Build rule for Quickstep app.
+android_app {
+ name: "Launcher3QuickStep",
+
+ static_libs: ["Launcher3QuickStepLib"],
+ optimize: {
+ enabled: false,
+ },
+
+ platform_apis: true,
+ min_sdk_version: "current",
+ target_sdk_version: "current",
+
+ privileged: true,
+ system_ext_specific: true,
+ overrides: [
+ "Home",
+ "Launcher2",
+ "Launcher3",
+ ],
+ required: ["privapp_whitelist_com.android.launcher3"],
+
+ resource_dirs: ["quickstep/res"],
+
+ additional_manifests: [
+ "quickstep/AndroidManifest-launcher.xml",
+ "AndroidManifest-common.xml",
+ ],
+
+ manifest: "quickstep/AndroidManifest.xml",
+ jacoco: {
+ include_filter: ["com.android.launcher3.*"],
+ }
+
+}
+
+// Build rule for Launcher3 Go app with quickstep for Android Go devices.
+android_app {
+ name: "Launcher3QuickStepGo",
+
+ static_libs: [
+ "SystemUI-statsd",
+ "SystemUISharedLib",
+ "LauncherGoResLib",
+ ],
+
+ platform_apis: true,
+ min_sdk_version: "current",
+ target_sdk_version: "current",
+
+ srcs: [
+ "src/**/*.java",
+ "quickstep/src/**/*.java",
+ "go/src/**/*.java",
+ "go/quickstep/src/**/*.java",
+ ],
+
+ resource_dirs: [
+ "go/quickstep/res",
+ "go/res",
+ "quickstep/res",
+ ],
+
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ enabled: true,
+ },
+
+ privileged: true,
+ system_ext_specific: true,
+ overrides: [
+ "Home",
+ "Launcher2",
+ "Launcher3",
+ "Launcher3QuickStep",
+ ],
+ required: ["privapp_whitelist_com.android.launcher3"],
+
+ additional_manifests: [
+ "go/AndroidManifest.xml",
+ "go/AndroidManifest-launcher.xml",
+ "AndroidManifest-common.xml",
+ ],
+
+ manifest: "quickstep/AndroidManifest.xml",
+ jacoco: {
+ include_filter: ["com.android.launcher3.*"],
+ }
+
+}
+
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 1bc8b28..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-#
-# Build rule for Launcher3 Go app for Android Go devices.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, src_ui_overrides) \
- $(call all-java-files-under, go/src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 26
-LOCAL_PACKAGE_NAME := Launcher3Go
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/AndroidManifest.xml \
- $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := go/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-#
-# Build rule for Quickstep app.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3QuickStepLib
-LOCAL_PROGUARD_ENABLED := disabled
-
-ifneq (,$(wildcard frameworks/base))
- LOCAL_PRIVATE_PLATFORM_APIS := true
-else
- LOCAL_SDK_VERSION := system_current
- LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_PACKAGE_NAME := Launcher3QuickStep
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
- $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-
-#
-# Build rule for Launcher3 Go app with quickstep for Android Go devices.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- SystemUI-statsd \
- SystemUISharedLib
-ifneq (,$(wildcard frameworks/base))
- LOCAL_PRIVATE_PLATFORM_APIS := true
-else
- LOCAL_SDK_VERSION := system_current
- LOCAL_MIN_SDK_VERSION := 26
-endif
-LOCAL_STATIC_ANDROID_LIBRARIES := LauncherGoResLib
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, go/src) \
- $(call all-java-files-under, go/quickstep/src)
-
-LOCAL_RESOURCE_DIR := \
- $(LOCAL_PATH)/go/quickstep/res \
- $(LOCAL_PATH)/go/res \
- $(LOCAL_PATH)/quickstep/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_PROGUARD_ENABLED := full
-
-LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := \
- $(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
- $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_LICENSE_PACKAGE_NAME := Android Launcher3
-LOCAL_NOTICE_FILE := build/soong/licenses/LICENSE
-include $(BUILD_PACKAGE)
-
-
-# ==================================================
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/OWNERS b/OWNERS
index 7f98ea6..560b562 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,13 +7,6 @@
alexchau@google.com
andraskloczl@google.com
patmanning@google.com
-petrcermak@google.com
-pbdr@google.com
-kideckel@google.com
-stevenckng@google.com
-ydixit@google.com
-boadway@google.com
-alinazaidi@google.com
adamcohen@google.com
hyunyoungs@google.com
mrcasey@google.com
@@ -24,10 +17,8 @@
zakcohen@google.com
santie@google.com
vadimt@google.com
-mett@google.com
jonmiranda@google.com
pinyaoting@google.com
-sfufa@google.com
gwasserman@google.com
jamesoleary@google.com
joshtrask@google.com
@@ -37,8 +28,6 @@
tracyzhou@google.com
peanutbutter@google.com
xuqiu@google.com
-sreyasr@google.com
-thiruram@google.com
brianji@google.com
per-file FeatureFlags.java, globs = set noparent
diff --git a/protos/view_capture.proto b/protos/view_capture.proto
new file mode 100644
index 0000000..98574dd
--- /dev/null
+++ b/protos/view_capture.proto
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.launcher3.view;
+
+option java_outer_classname = "ViewCaptureData";
+
+message ExportedData {
+
+ repeated FrameData frameData = 1;
+}
+
+message FrameData {
+ optional int64 timestamp = 1;
+ optional ViewNode node = 2;
+}
+
+message ViewNode {
+ optional string classname = 1;
+ optional string id = 2;
+ optional int32 left = 3;
+ optional int32 top = 4;
+ optional int32 width = 5;
+ optional int32 height = 6;
+ optional int32 scrollX = 7;
+ optional int32 scrollY = 8;
+
+ optional float translationX = 9;
+ optional float translationY = 10;
+ optional float scaleX = 11 [default = 1];
+ optional float scaleY = 12 [default = 1];
+ optional float alpha = 13 [default = 1];
+
+ optional bool willNotDraw = 14;
+ optional bool clipChildren = 15;
+ optional int32 visibility = 16;
+
+ repeated ViewNode children = 17;
+}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 70b1438..f739f81 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -26,7 +26,7 @@
filegroup {
name: "launcher3-quickstep-tests-src",
path: "tests",
- srcs: ["tests/src/**/*.java"],
+ srcs: ["tests/src/**/*.java", "tests/src/**/*.kt"],
}
filegroup {
diff --git a/quickstep/res/drawable/split_instructions_background.xml b/quickstep/res/drawable/split_instructions_background.xml
new file mode 100644
index 0000000..6d0e7db
--- /dev/null
+++ b/quickstep/res/drawable/split_instructions_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="@dimen/split_instructions_radius" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/split_instructions_view.xml b/quickstep/res/layout/split_instructions_view.xml
new file mode 100644
index 0000000..91fb05c
--- /dev/null
+++ b/quickstep/res/layout/split_instructions_view.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.quickstep.views.SplitInstructionsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/split_instructions_background"
+ android:paddingRight="@dimen/split_instructions_horizontal_padding"
+ android:paddingLeft="@dimen/split_instructions_horizontal_padding"
+ android:paddingTop="@dimen/split_instructions_vertical_padding"
+ android:paddingBottom="@dimen/split_instructions_vertical_padding"
+ android:elevation="@dimen/split_instructions_elevation"
+ android:visibility="gone">
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/split_instructions_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ android:textColor="?androidprv:attr/textColorOnAccent"
+ android:text="@string/toast_split_select_app" />
+</com.android.quickstep.views.SplitInstructionsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 3b1d217..94388b4 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -45,8 +45,8 @@
android:id="@+id/start_contextual_buttons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
- android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+ android:paddingStart="@dimen/taskbar_contextual_button_padding"
+ android:paddingEnd="@dimen/taskbar_contextual_button_padding"
android:paddingTop="@dimen/taskbar_contextual_padding_top"
android:gravity="center_vertical"
android:layout_gravity="start"/>
@@ -56,9 +56,6 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
- android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
- android:layout_marginEnd="@dimen/taskbar_contextual_button_margin"
android:gravity="center_vertical"
android:layout_gravity="end"/>
@@ -66,8 +63,6 @@
android:id="@+id/end_contextual_buttons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
- android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
android:paddingTop="@dimen/taskbar_contextual_padding_top"
android:gravity="center_vertical"
android:layout_gravity="end"/>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index f233bde..8368069 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<resources>
- <dimen name="overview_task_margin">8dp</dimen>
+ <!-- Overview actions -->
+ <dimen name="overview_actions_top_margin">12dp</dimen>
<!-- Tips Gesture Tutorial -->
<dimen name="gesture_tutorial_feedback_margin_start_end">126dp</dimen>
@@ -73,4 +74,10 @@
<!-- Gesture Tutorial mock taskbar -->
<dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
+
+ <!-- Taskbar 3 button spacing -->
+ <dimen name="taskbar_button_margin_5_5">94.5dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">94.5dp</dimen>
+ <dimen name="taskbar_button_margin_4_5">84dp</dimen>
+ <dimen name="taskbar_button_margin_4_4">79dp</dimen>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3072a3e..0232f86 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -36,6 +36,7 @@
<dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
<dimen name="overview_task_margin">16dp</dimen>
<dimen name="overview_page_spacing">16dp</dimen>
+ <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
<item name="overview_max_scale" format="float" type="dimen">0.7</item>
<item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
@@ -66,7 +67,6 @@
<dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
<!-- Launcher app transition -->
- <item name="content_scale" format="float" type="dimen">0.97</item>
<dimen name="closing_window_trans_y">115dp</dimen>
<dimen name="quick_switch_scaling_scroll_threshold">100dp</dimen>
@@ -239,7 +239,7 @@
<dimen name="navigation_key_padding">0dp</dimen>
<!-- Floating rotation button -->
- <dimen name="floating_rotation_button_diameter">40dp</dimen>
+ <dimen name="floating_rotation_button_diameter">52dp</dimen>
<dimen name="floating_rotation_button_min_margin">20dp</dimen>
<dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
<dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
@@ -250,11 +250,11 @@
<dimen name="taskbar_icon_touch_size">48dp</dimen>
<dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
<dimen name="taskbar_folder_margin">16dp</dimen>
- <dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
+ <dimen name="taskbar_contextual_button_padding">16dp</dimen>
<dimen name="taskbar_contextual_padding_top">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
- <dimen name="taskbar_contextual_button_margin">40dp</dimen>
- <dimen name="taskbar_hotseat_nav_spacing">42dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">47dp</dimen>
+ <dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
<dimen name="taskbar_contextual_buttons_size">35dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
@@ -268,4 +268,12 @@
<dimen name="taskbar_back_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_icon_size_kids">32dp</dimen>
+
+ <!-- Taskbar 3 button spacing -->
+ <dimen name="taskbar_button_space_inbetween">24dp</dimen>
+ <dimen name="taskbar_button_margin_5_5">26dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">26dp</dimen>
+ <dimen name="taskbar_button_margin_4_5">47dp</dimen>
+ <dimen name="taskbar_button_margin_4_4">47dp</dimen>
+ <dimen name="taskbar_button_margin_default">47dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2239102..e21dcba 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -20,6 +20,8 @@
import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -70,6 +72,7 @@
import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.UiThreadHelper;
@@ -91,7 +94,10 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
+import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -127,9 +133,19 @@
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
+ /**
+ * If Launcher restarted while in the middle of an Overview split select, it needs this data to
+ * recover. In all other cases this will remain null.
+ */
+ private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPendingSplitSelectInfo = ObjectWrapper.unwrap(
+ savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
+ }
addMultiWindowModeChangedListener(mDepthController);
initUnfoldTransitionProgressProvider();
}
@@ -343,15 +359,17 @@
}
private void initUnfoldTransitionProgressProvider() {
- final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
+ final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
if (config.isEnabled()) {
mUnfoldTransitionProgressProvider =
UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
- this,
+ /* context= */ this,
config,
ProxyScreenStatusProvider.INSTANCE,
- getSystemService(DeviceStateManager.class),
- getSystemService(ActivityManager.class),
+ new DeviceStateManagerFoldProvider(
+ getSystemService(DeviceStateManager.class), /* context */this),
+ new ActivityManagerActivityTypeProvider(
+ getSystemService(ActivityManager.class)),
getSystemService(SensorManager.class),
getMainThreadHandler(),
getMainExecutor(),
@@ -638,4 +656,53 @@
mDepthController.dump(prefix, writer);
}
}
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ // If Launcher shuts downs during split select, we save some extra data in the recovery
+ // bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
+ // work in this case because restoring straight to OverviewSplitSelect without staging data,
+ // or before the tasks themselves have loaded into Overview, causes a crash. So we tell
+ // Launcher to first restore into Overview state, wait for the relevant tasks and icons to
+ // load in, and then proceed to OverviewSplitSelect.
+ if (isInState(OVERVIEW_SPLIT_SELECT)) {
+ SplitSelectStateController splitSelectStateController =
+ ((RecentsView) getOverviewPanel()).getSplitPlaceholder();
+ // Launcher will restart in Overview and then transition to OverviewSplitSelect.
+ outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
+ new PendingSplitSelectInfo(
+ splitSelectStateController.getInitialTaskId(),
+ splitSelectStateController.getActiveSplitStagePosition()
+ )
+ ));
+ outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
+ }
+ }
+
+ /**
+ * When Launcher restarts, it sometimes needs to recover to a split selection state.
+ * This function checks if such a recovery is needed.
+ * @return a boolean representing whether the launcher is waiting to recover to
+ * OverviewSplitSelect state.
+ */
+ public boolean hasPendingSplitSelectInfo() {
+ return mPendingSplitSelectInfo != null;
+ }
+
+ /**
+ * See {@link #hasPendingSplitSelectInfo()}
+ */
+ public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
+ return mPendingSplitSelectInfo;
+ }
+
+ /**
+ * When the launcher has successfully recovered to OverviewSplitSelect state, this function
+ * deletes the recovery data, returning it to a null state.
+ */
+ public void finishSplitSelectRecovery() {
+ mPendingSplitSelectInfo = null;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index e1a3b72..bb79c1b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -31,7 +31,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.mapBoundToRange;
-import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
@@ -184,6 +183,10 @@
public static final int SPLIT_DIVIDER_ANIM_DURATION = 100;
public static final int CONTENT_ALPHA_DURATION = 217;
+ public static final int TASKBAR_TO_APP_DURATION = 600;
+ // TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
+ // is solved.
+ public static final int TASKBAR_TO_HOME_DURATION = 300;
protected static final int CONTENT_SCALE_DURATION = 350;
protected static final int CONTENT_SCRIM_DURATION = 350;
@@ -197,7 +200,6 @@
final Handler mHandler;
- private final float mContentScale;
private final float mClosingWindowTransY;
private final float mMaxShadowRadius;
@@ -242,7 +244,6 @@
mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
Resources res = mLauncher.getResources();
- mContentScale = res.getFloat(R.dimen.content_scale);
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
@@ -480,8 +481,8 @@
: new float[]{0, 1};
float[] scales = isAppOpening
- ? new float[]{1, mContentScale}
- : new float[]{mContentScale, 1};
+ ? new float[]{1, mDeviceProfile.workspaceContentScale}
+ : new float[]{mDeviceProfile.workspaceContentScale, 1};
// Pause expensive view updates as they can lead to layer thrashing and skipped frames.
mLauncher.pauseExpensiveViewUpdates();
@@ -527,7 +528,15 @@
workspace.forEachVisiblePage(
view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
- viewsToAnimate.add(mLauncher.getHotseat());
+ // Do not scale hotseat as a whole when taskbar is present, and scale QSB only if it's
+ // not inline.
+ if (mDeviceProfile.isTaskbarPresent) {
+ if (!mDeviceProfile.isQsbInline) {
+ viewsToAnimate.add(mLauncher.getHotseat().getQsb());
+ }
+ } else {
+ viewsToAnimate.add(mLauncher.getHotseat());
+ }
viewsToAnimate.forEach(view -> {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 0284ae4..f42b39f 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -21,7 +21,6 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Build;
import android.text.Layout;
@@ -33,7 +32,6 @@
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
@@ -239,12 +237,6 @@
}
@Override
- public void setInsets(Rect insets, DeviceProfile grid) {
- int leftRightPadding = grid.allAppsLeftRightPadding;
- setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
- }
-
- @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 1dec737..351a3bc 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -19,7 +19,6 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -252,12 +251,6 @@
}
@Override
- public void setInsets(Rect insets, DeviceProfile grid) {
- int leftRightPadding = grid.allAppsLeftRightPadding;
- setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
- }
-
- @Override
public Class<PredictionRowView> getTypeClass() {
return PredictionRowView.class;
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 119ae90..7b48332 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -41,7 +41,6 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.views.AbstractSlideInView;
@@ -107,8 +106,7 @@
mDismissBtn.setOnClickListener(this::onDismiss);
LinearLayout buttonContainer = findViewById(R.id.button_container);
- int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context)
- - buttonContainer.getPaddingEnd();
+ int adjustedMarginEnd = grid.hotseatBarEndOffset - buttonContainer.getPaddingEnd();
if (InvariantDeviceProfile.INSTANCE.get(context)
.getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) {
((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ca30e72..6df31e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -171,7 +171,9 @@
isResumed,
fromInit,
/* startAnimation= */ true,
- QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
+ !isResumed
+ ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
+ : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
}
@Nullable
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 5d576f7..b01168d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -35,6 +35,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
import android.animation.ArgbEvaluator;
@@ -45,6 +46,7 @@
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Region;
@@ -66,6 +68,7 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -93,7 +96,7 @@
private final Rect mTempRect = new Rect();
- private static final int FLAG_SWITCHER_SUPPORTED = 1 << 0;
+ private static final int FLAG_SWITCHER_SHOWING = 1 << 0;
private static final int FLAG_IME_VISIBLE = 1 << 1;
private static final int FLAG_ROTATION_BUTTON_VISIBLE = 1 << 2;
private static final int FLAG_A11Y_VISIBLE = 1 << 3;
@@ -105,8 +108,7 @@
private static final int FLAG_DISABLE_BACK = 1 << 9;
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
-
- private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
+ private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
@@ -189,7 +191,7 @@
isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
mControllers.navButtonController, R.id.ime_switcher);
mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
- flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
+ flags -> ((flags & FLAG_SWITCHER_SHOWING) != 0)
&& ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
}
@@ -207,9 +209,12 @@
boolean isInKidsMode = mContext.isNavBarKidsModeActive();
boolean alwaysShowButtons = isThreeButtonNav || isInSetup;
- // Make sure to remove nav bar buttons translation when notification shade is expanded or
- // IME is showing (add separate translation for IME).
- int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE;
+ // Make sure to remove nav bar buttons translation when any of the following occur:
+ // - Notification shade is expanded
+ // - IME is showing (add separate translation for IME)
+ // - VoiceInteractionWindow (assistant) is showing
+ int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE
+ | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
mPropertyHolders.add(new StatePropertyHolder(mNavButtonInAppDisplayProgressForSysui,
flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
1, 0));
@@ -225,6 +230,7 @@
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
+ updateButtonLayoutSpacing();
if (isInSetup) {
// Since setup wizard only has back button enabled, it looks strange to be
@@ -415,7 +421,7 @@
return recentsCoords;
}, new Handler());
recentsButton.setOnClickListener(v -> {
- navButtonController.onButtonClick(BUTTON_RECENTS);
+ navButtonController.onButtonClick(BUTTON_RECENTS, v);
mHitboxExtender.onRecentsButtonClicked();
});
mPropertyHolders.add(new StatePropertyHolder(recentsButton,
@@ -443,22 +449,26 @@
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+ boolean isVoiceInteractionWindowShowing =
+ (sysUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0;
// TODO(b/202218289) we're getting IME as not visible on lockscreen from system
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
- updateStateForFlag(FLAG_SWITCHER_SUPPORTED, isImeSwitcherShowing);
+ updateStateForFlag(FLAG_SWITCHER_SHOWING, isImeSwitcherShowing);
updateStateForFlag(FLAG_A11Y_VISIBLE, a11yVisible);
updateStateForFlag(FLAG_DISABLE_HOME, isHomeDisabled);
updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
+ updateStateForFlag(FLAG_VOICE_INTERACTION_WINDOW_SHOWING, isVoiceInteractionWindowShowing);
if (mA11yButton != null) {
// Only used in 3 button
boolean a11yLongClickable =
(sysUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
mA11yButton.setLongClickable(a11yLongClickable);
+ updateButtonLayoutSpacing();
}
}
@@ -474,6 +484,13 @@
}
/**
+ * @return {@code true} if A11y is showing in 3 button nav taskbar
+ */
+ private boolean isContextualButtonShowing() {
+ return mContext.isThreeButtonNav() && (mState & FLAG_A11Y_VISIBLE) != 0;
+ }
+
+ /**
* Should be called when we need to show back button for bouncer
*/
public void setBackForBouncer(boolean isBouncerVisible) {
@@ -499,6 +516,13 @@
}
/**
+ * Returns true if IME switcher is visible
+ */
+ public boolean isImeSwitcherVisible() {
+ return (mState & FLAG_SWITCHER_SHOWING) != 0;
+ }
+
+ /**
* Returns true if the home button is disabled
*/
public boolean isHomeDisabled() {
@@ -626,9 +650,9 @@
buttonView.setImageResource(drawableId);
buttonView.setContentDescription(parent.getContext().getString(
navButtonController.getButtonContentDescription(buttonType)));
- buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+ buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view));
buttonView.setOnLongClickListener(view ->
- navButtonController.onButtonLongClick(buttonType));
+ navButtonController.onButtonLongClick(buttonType, view));
return buttonView;
}
@@ -649,6 +673,46 @@
if (mFloatingRotationButton != null) {
mFloatingRotationButton.onConfigurationChanged(configChanges);
}
+ updateButtonLayoutSpacing();
+ }
+
+ /** Adds the correct spacing to 3 button nav container. No-op if using gesture nav */
+ private void updateButtonLayoutSpacing() {
+ if (!mContext.isThreeButtonNav()) {
+ return;
+ }
+ DeviceProfile dp = mContext.getDeviceProfile();
+ Resources res = mContext.getResources();
+
+ // Add spacing after the end of the last nav button
+ FrameLayout.LayoutParams navButtonParams =
+ (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
+ int navMarginEnd = (int) res.getDimension(dp.inv.inlineNavButtonsEndSpacing);
+ int contextualWidth = mEndContextualContainer.getWidth();
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
+ }
+ navButtonParams.setMarginEnd(navMarginEnd);
+ mNavButtonContainer.setLayoutParams(navButtonParams);
+
+ // Add the spaces in between the nav buttons
+ int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween);
+ for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
+ View navButton = mNavButtonContainer.getChildAt(i);
+ LinearLayout.LayoutParams buttonLayoutParams =
+ (LinearLayout.LayoutParams) navButton.getLayoutParams();
+ if (i == 0) {
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ } else if (i == mNavButtonContainer.getChildCount() - 1) {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ } else {
+ buttonLayoutParams.setMarginStart(spaceInBetween / 2);
+ buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
+ }
+ }
}
public void onDestroy() {
@@ -721,22 +785,17 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "NavbarButtonsViewController:");
- pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
- pw.println(String.format(
- "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor)));
- pw.println(String.format(
- "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor)));
- pw.println(String.format(
- "%s\tmFloatingRotationButtonBounds=%s", prefix, mFloatingRotationButtonBounds));
- pw.println(String.format(
- "%s\tmSysuiStateFlags=%s",
- prefix,
- QuickStepContract.getSystemUiStateString(mSysuiStateFlags)));
+ pw.println(prefix + "\tmState=" + getStateString(mState));
+ pw.println(prefix + "\tmLightIconColor=" + Integer.toHexString(mLightIconColor));
+ pw.println(prefix + "\tmDarkIconColor=" + Integer.toHexString(mDarkIconColor));
+ pw.println(prefix + "\tmFloatingRotationButtonBounds=" + mFloatingRotationButtonBounds);
+ pw.println(prefix + "\tmSysuiStateFlags=" + QuickStepContract.getSystemUiStateString(
+ mSysuiStateFlags));
}
private static String getStateString(int flags) {
StringJoiner str = new StringJoiner("|");
- appendFlag(str, flags, FLAG_SWITCHER_SUPPORTED, "FLAG_SWITCHER_SUPPORTED");
+ appendFlag(str, flags, FLAG_SWITCHER_SHOWING, "FLAG_SWITCHER_SHOWING");
appendFlag(str, flags, FLAG_IME_VISIBLE, "FLAG_IME_VISIBLE");
appendFlag(str, flags, FLAG_ROTATION_BUTTON_VISIBLE, "FLAG_ROTATION_BUTTON_VISIBLE");
appendFlag(str, flags, FLAG_A11Y_VISIBLE, "FLAG_A11Y_VISIBLE");
@@ -750,6 +809,8 @@
appendFlag(str, flags, FLAG_NOTIFICATION_SHADE_EXPANDED,
"FLAG_NOTIFICATION_SHADE_EXPANDED");
appendFlag(str, flags, FLAG_SCREEN_PINNING_ACTIVE, "FLAG_SCREEN_PINNING_ACTIVE");
+ appendFlag(str, flags, FLAG_VOICE_INTERACTION_WINDOW_SHOWING,
+ "FLAG_VOICE_INTERACTION_WINDOW_SHOWING");
return str.toString();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index b797807..800e162 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -43,7 +43,8 @@
public static final int ALPHA_INDEX_STASHED = 0;
public static final int ALPHA_INDEX_HOME_DISABLED = 1;
- private static final int NUM_ALPHA_CHANNELS = 2;
+ public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 2;
+ private static final int NUM_ALPHA_CHANNELS = 3;
/**
* The SharedPreferences key for whether the stashed handle region is dark.
@@ -208,10 +209,9 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "StashedHandleViewController:");
- pw.println(String.format(
- "%s\tisStashedHandleVisible=%b", prefix, isStashedHandleVisible()));
- pw.println(String.format("%s\tmStashedHandleWidth=%dpx", prefix, mStashedHandleWidth));
- pw.println(String.format("%s\tmStashedHandleHeight=%dpx", prefix, mStashedHandleHeight));
+ pw.println(prefix + "\tisStashedHandleVisible=" + isStashedHandleVisible());
+ pw.println(prefix + "\tmStashedHandleWidth=" + mStashedHandleWidth);
+ pw.println(prefix + "\tmStashedHandleHeight=" + mStashedHandleHeight);
mRegionSamplingHelper.dump(prefix, pw);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index dc2e3b6..439490e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
@@ -200,7 +201,8 @@
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
new TaskbarAllAppsController(this, dp),
- new TaskbarInsetsController(this));
+ new TaskbarInsetsController(this),
+ new VoiceInteractionWindowController(this));
}
public void init(@NonNull TaskbarSharedState sharedState) {
@@ -246,14 +248,23 @@
return super.getStatsLogManager();
}
- /** Creates LayoutParams for adding a view directly to WindowManager as a new window */
+ /** @see #createDefaultWindowLayoutParams(int) */
public WindowManager.LayoutParams createDefaultWindowLayoutParams() {
+ return createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL);
+ }
+
+ /**
+ * Creates LayoutParams for adding a view directly to WindowManager as a new window.
+ * @param type The window type to pass to the created WindowManager.LayoutParams.
+ */
+ public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type) {
WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT,
mLastRequestedNonFullscreenHeight,
- TYPE_NAVIGATION_BAR_PANEL,
+ type,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SLIPPERY,
+ | WindowManager.LayoutParams.FLAG_SLIPPERY
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
windowLayoutParams.setTitle(WINDOW_TITLE);
windowLayoutParams.packageName = getPackageName();
@@ -453,6 +464,8 @@
fromInit);
mControllers.taskbarViewController.setImeIsVisible(
mControllers.navbarButtonsViewController.isImeVisible());
+ mControllers.taskbarViewController.setIsImeSwitcherVisible(
+ mControllers.navbarButtonsViewController.isImeSwitcherVisible());
int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
@@ -468,6 +481,8 @@
fromInit);
mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
+ mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
+ (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
}
/**
@@ -612,7 +627,9 @@
/** Removes the given view from WindowManager. See {@link #addWindowView}. */
public void removeWindowView(View view) {
- mWindowManager.removeViewImmediate(view);
+ if (view.isAttachedToWindow()) {
+ mWindowManager.removeViewImmediate(view);
+ }
}
protected void onTaskbarIconClicked(View view) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
index c5615c7..3cf9c99 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -71,8 +71,7 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarAutohideSuspendController:");
- pw.println(String.format(
- "%s\tmAutohideSuspendFlags=%s", prefix, getStateString(mAutohideSuspendFlags)));
+ pw.println(prefix + "\tmAutohideSuspendFlags=" + getStateString(mAutohideSuspendFlags));
}
private static String getStateString(int flags) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 449e0a7..d7b50b0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -52,6 +52,7 @@
public final TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController;
public final TaskbarAllAppsController taskbarAllAppsController;
public final TaskbarInsetsController taskbarInsetsController;
+ public final VoiceInteractionWindowController voiceInteractionWindowController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -80,7 +81,8 @@
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
TaskbarAllAppsController taskbarAllAppsController,
- TaskbarInsetsController taskbarInsetsController) {
+ TaskbarInsetsController taskbarInsetsController,
+ VoiceInteractionWindowController voiceInteractionWindowController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -99,6 +101,7 @@
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
this.taskbarAllAppsController = taskbarAllAppsController;
this.taskbarInsetsController = taskbarInsetsController;
+ this.voiceInteractionWindowController = voiceInteractionWindowController;
}
/**
@@ -126,13 +129,15 @@
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
navButtonController.init(this);
taskbarInsetsController.init(this);
+ voiceInteractionWindowController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
taskbarDragLayerController, taskbarScrimViewController, taskbarViewController,
taskbarUnfoldAnimationController, taskbarKeyguardController,
stashedHandleViewController, taskbarStashController, taskbarEduController,
- taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController
+ taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
+ voiceInteractionWindowController
};
mAreAllControllersInitialized = true;
@@ -172,6 +177,7 @@
taskbarAllAppsController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
+ voiceInteractionWindowController.onDestroy();
mControllersToLog = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index c522888..3a0f374 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import android.animation.Animator;
@@ -435,7 +436,7 @@
if (tag instanceof ItemInfo) {
ItemInfo item = (ItemInfo) tag;
TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
- if (item.container == CONTAINER_ALL_APPS) {
+ if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
// Since all apps closes when the drag starts, target the all apps button instead.
target = taskbarViewController.getAllAppsButtonView();
} else if (item.container >= 0) {
@@ -558,13 +559,11 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarDragController:");
- pw.println(String.format("%s\tmDragIconSize=%dpx", prefix, mDragIconSize));
- pw.println(String.format("%s\tmTempXY=%s", prefix, Arrays.toString(mTempXY)));
- pw.println(String.format("%s\tmRegistrationX=%d", prefix, mRegistrationX));
- pw.println(String.format("%s\tmRegistrationY=%d", prefix, mRegistrationY));
- pw.println(String.format(
- "%s\tmIsSystemDragInProgress=%b", prefix, mIsSystemDragInProgress));
- pw.println(String.format(
- "%s\tisInternalDragInProgess=%b", prefix, super.isDragging()));
+ pw.println(prefix + "\tmDragIconSize=" + mDragIconSize);
+ pw.println(prefix + "\tmTempXY=" + Arrays.toString(mTempXY));
+ pw.println(prefix + "\tmRegistrationX=" + mRegistrationX);
+ pw.println(prefix + "\tmRegistrationY=" + mRegistrationY);
+ pw.println(prefix + "\tmIsSystemDragInProgress=" + mIsSystemDragInProgress);
+ pw.println(prefix + "\tisInternalDragInProgess=" + super.isDragging());
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 99c59a8..77ef83c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -144,10 +144,9 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarDragLayerController:");
- pw.println(String.format("%s\tmBgOffset=%.2f", prefix, mBgOffset.value));
- pw.println(String.format("%s\tmFolderMargin=%dpx", prefix, mFolderMargin));
- pw.println(String.format(
- "%s\tmLastSetBackgroundAlpha=%.2f", prefix, mLastSetBackgroundAlpha));
+ pw.println(prefix + "\tmBgOffset=" + mBgOffset.value);
+ pw.println(prefix + "\tmFolderMargin=" + mFolderMargin);
+ pw.println(prefix + "\tmLastSetBackgroundAlpha=" + mLastSetBackgroundAlpha);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index e29b14b..32a3c10 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -191,12 +191,10 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarEduController:");
- pw.println(String.format("%s\tisShowingEdu=%b", prefix, mTaskbarEduView != null));
- pw.println(String.format("%s\tmWaveAnimTranslationY=%.2f", prefix, mWaveAnimTranslationY));
- pw.println(String.format(
- "%s\tmWaveAnimTranslationYReturnOvershoot=%.2f",
- prefix,
- mWaveAnimTranslationYReturnOvershoot));
+ pw.println(prefix + "\tisShowingEdu=" + (mTaskbarEduView != null));
+ pw.println(prefix + "\tmWaveAnimTranslationY=" + mWaveAnimTranslationY);
+ pw.println(prefix + "\tmWaveAnimTranslationYReturnOvershoot="
+ + mWaveAnimTranslationYReturnOvershoot);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 6a6a693..48fde8f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -17,8 +17,12 @@
import android.graphics.Insets
import android.graphics.Region
+import android.view.InsetsFrameProvider
import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
+import android.view.InsetsState
import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
+import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
import com.android.launcher3.DeviceProfile
@@ -61,9 +65,6 @@
)
)
- windowLayoutParams.providedInternalInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
- windowLayoutParams.providedInternalImeInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
-
onTaskbarWindowHeightOrInsetsChanged()
windowLayoutParams.insetsRoundedCornerFrame = true
@@ -75,32 +76,35 @@
}
fun onTaskbarWindowHeightOrInsetsChanged() {
- var reducingSize = getReducingInsetsForTaskbarInsetsHeight(
- controllers.taskbarStashController.contentHeightToReportToApps)
+ var contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
+ contentRegion.set(0, windowLayoutParams.height - contentHeight,
+ context.deviceProfile.widthPx, windowLayoutParams.height)
+ var tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
+ for (provider in windowLayoutParams.providedInsets) {
+ if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR) {
+ provider.insetsSize = Insets.of(0, 0, 0, contentHeight)
+ } else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT
+ || provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
+ provider.insetsSize = Insets.of(0, 0, 0, tappableHeight)
+ }
+ }
- contentRegion.set(0, reducingSize.top,
- context.deviceProfile.widthPx, windowLayoutParams.height)
- windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
- windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
- reducingSize = getReducingInsetsForTaskbarInsetsHeight(
- controllers.taskbarStashController.tappableHeightToReportToApps)
- windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
- windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
-
- reducingSize = getReducingInsetsForTaskbarInsetsHeight(taskbarHeightForIme)
- windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR] = reducingSize
- windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT] = reducingSize
- windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_MANDATORY_GESTURES] = reducingSize
- }
-
- /**
- * WindowLayoutParams.providedInternal*Insets expects Insets that subtract from the window frame
- * height (i.e. WindowLayoutParams#height). So for Taskbar to report bottom insets to apps, it
- * actually provides insets from the top of its window frame.
- * @param height The number of pixels from the bottom of the screen that Taskbar insets.
- */
- private fun getReducingInsetsForTaskbarInsetsHeight(height: Int): Insets {
- return Insets.of(0, windowLayoutParams.height - height, 0, 0)
+ val imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+ // Use 0 insets for the VoiceInteractionWindow (assistant) when gesture nav is enabled.
+ val visInsetsSize = Insets.of(0, 0, 0, if (context.isGestureNav) 0 else tappableHeight)
+ val insetsSizeOverride = arrayOf(
+ InsetsFrameProvider.InsetsSizeOverride(
+ TYPE_INPUT_METHOD,
+ imeInsetsSize
+ ),
+ InsetsFrameProvider.InsetsSizeOverride(
+ TYPE_VOICE_INTERACTION,
+ visInsetsSize
+ )
+ )
+ for (provider in windowLayoutParams.providedInsets) {
+ provider.insetsSizeOverrides = insetsSizeOverride
+ }
}
/**
@@ -151,13 +155,18 @@
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println(prefix + "TaskbarInsetsController:")
pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
- pw.println("$prefix\tprovidedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" +
- "${windowLayoutParams.providedInternalInsets[ITYPE_EXTRA_NAVIGATION_BAR]}")
- pw.println("$prefix\tprovidedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" +
- "${windowLayoutParams.providedInternalInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}")
- pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]=" +
- "${windowLayoutParams.providedInternalImeInsets[ITYPE_EXTRA_NAVIGATION_BAR]}")
- pw.println("$prefix\tprovidedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]=" +
- "${windowLayoutParams.providedInternalImeInsets[ITYPE_BOTTOM_TAPPABLE_ELEMENT]}")
+ for (provider in windowLayoutParams.providedInsets) {
+ pw.print("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
+ + " insetsSize=" + provider.insetsSize)
+ if (provider.insetsSizeOverrides != null) {
+ pw.print(" insetsSizeOverrides={")
+ for ((i, overrideSize) in provider.insetsSizeOverrides.withIndex()) {
+ if (i > 0) pw.print(", ")
+ pw.print(overrideSize)
+ }
+ pw.print("})")
+ }
+ pw.println()
+ }
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
index 56648ea..0808fab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java
@@ -111,11 +111,9 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarKeyguardController:");
- pw.println(String.format(
- "%s\tmKeyguardSysuiFlags=%s",
- prefix,
- QuickStepContract.getSystemUiStateString(mKeyguardSysuiFlags)));
- pw.println(String.format("%s\tmBouncerShowing=%b", prefix, mBouncerShowing));
- pw.println(String.format("%s\tmIsScreenOff=%b", prefix, mIsScreenOff));
+ pw.println(prefix + "\tmKeyguardSysuiFlags=" + QuickStepContract.getSystemUiStateString(
+ mKeyguardSysuiFlags));
+ pw.println(prefix + "\tmBouncerShowing=" + mBouncerShowing);
+ pw.println(prefix + "\tmIsScreenOff=" + mIsScreenOff);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index dc0ef27..85e76b2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -19,11 +19,13 @@
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -31,7 +33,9 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
@@ -44,7 +48,6 @@
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
-import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -53,6 +56,9 @@
*/
public class TaskbarLauncherStateController {
+ private static final String TAG = TaskbarLauncherStateController.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
public static final int FLAG_RESUMED = 1 << 0;
public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
@@ -99,7 +105,11 @@
}
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
if (!mShouldDelayLauncherStateAnim) {
- applyState();
+ if (toState == LauncherState.NORMAL) {
+ applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
+ } else {
+ applyState();
+ }
}
}
@@ -122,7 +132,12 @@
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mIconAlphaForHome.setConsumer(
- (Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
+ alpha -> {
+ mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+ if (mLauncher.getDeviceProfile().isQsbInline) {
+ mLauncher.getHotseat().setQsbAlpha(alpha > 0 ? 0 : 1);
+ }
+ });
mIconAlignmentForResumedState.finishAnimation();
onIconAlignmentRatioChangedForAppAndHomeTransition();
@@ -169,10 +184,8 @@
mTaskBarRecentsAnimationListener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(mTaskBarRecentsAnimationListener);
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.setTaskLaunchListener(() -> {
- mTaskBarRecentsAnimationListener.endGestureStateOverride(true);
- });
+ ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
+ mTaskBarRecentsAnimationListener.endGestureStateOverride(true));
return animatorSet;
}
@@ -269,6 +282,11 @@
ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
.animateToValue(toAlignmentForResumedState)
.setDuration(duration);
+ if (DEBUG) {
+ Log.d(TAG, "mIconAlignmentForResumedState - "
+ + mIconAlignmentForResumedState.value
+ + " -> " + toAlignmentForResumedState + ": " + duration);
+ }
resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -305,6 +323,11 @@
if (isRecentsAnimationRunning) {
gestureAlignAnim.setDuration(duration);
}
+ if (DEBUG) {
+ Log.d(TAG, "mIconAlignmentForGestureState - "
+ + mIconAlignmentForGestureState.value
+ + " -> " + toAlignmentForGestureState + ": " + duration);
+ }
gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -330,6 +353,7 @@
.setDuration(duration));
}
+ animatorSet.setInterpolator(EMPHASIZED);
if (start) {
animatorSet.start();
}
@@ -344,11 +368,14 @@
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
- float toAlignment = mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) ? 1 : 0;
+ boolean willStashVisually =
+ isInStashedState && mControllers.taskbarStashController.supportsVisualStashing();
+ float toAlignment =
+ mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) && !willStashVisually ? 1 : 0;
- TaskbarStashController controller = mControllers.taskbarStashController;
- controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
- Animator stashAnimator = controller.applyStateWithoutStart(duration);
+ TaskbarStashController stashController = mControllers.taskbarStashController;
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
+ Animator stashAnimator = stashController.applyStateWithoutStart(duration);
if (stashAnimator != null) {
stashAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -368,12 +395,22 @@
});
animatorSet.play(stashAnimator);
}
-
- // If we're already animating to the value, just leave it be instead of restarting it.
+ if (mIconAlignmentForLauncherState.value == toAlignment) {
+ // Already at expected value, but make sure we run the callback at the end.
+ animatorSet.addListener(AnimatorListeners.forEndCallback(
+ this::onIconAlignmentRatioChangedForStateTransition));
+ }
if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) {
+ // If we're already animating to the value, just leave it be instead of restarting it.
mIconAlignmentForLauncherState.finishAnimation();
animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
.setDuration(duration));
+ if (DEBUG) {
+ Log.d(TAG, "mIconAlignmentForLauncherState - "
+ + mIconAlignmentForLauncherState.value
+ + " -> " + toAlignment + ": " + duration);
+ }
+ animatorSet.setInterpolator(EMPHASIZED);
}
}
@@ -396,17 +433,17 @@
onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
}
- private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) {
+ private void onIconAlignmentRatioChanged(Supplier<AnimatedFloat> alignmentSupplier) {
if (mControllers == null) {
return;
}
- float alignment = alignmentSupplier.get();
+ AnimatedFloat animatedFloat = alignmentSupplier.get();
float currentValue = mIconAlphaForHome.getValue();
- boolean taskbarWillBeVisible = alignment < 1;
+ boolean taskbarWillBeVisible = animatedFloat.value < 1;
boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
- updateIconAlignment(alignment);
+ updateIconAlignment(animatedFloat.value, animatedFloat.getEndValue());
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -416,21 +453,22 @@
}
}
- private void updateIconAlignment(float alignment) {
+ private void updateIconAlignment(float alignment, Float endAlignment) {
mControllers.taskbarViewController.setLauncherIconAlignment(
- alignment, mLauncher.getDeviceProfile());
+ alignment, endAlignment, mLauncher.getDeviceProfile());
// Switch taskbar and hotseat in last frame
setTaskbarViewVisible(alignment < 1);
mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment);
}
- private float getCurrentIconAlignmentRatioBetweenAppAndHome() {
- return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
+ private AnimatedFloat getCurrentIconAlignmentRatioBetweenAppAndHome() {
+ return mIconAlignmentForResumedState.value > mIconAlignmentForGestureState.value
+ ? mIconAlignmentForResumedState : mIconAlignmentForGestureState;
}
- private float getCurrentIconAlignmentRatioForLauncherState() {
- return mIconAlignmentForLauncherState.value;
+ private AnimatedFloat getCurrentIconAlignmentRatioForLauncherState() {
+ return mIconAlignmentForLauncherState;
}
private void setTaskbarViewVisible(boolean isVisible) {
@@ -459,6 +497,7 @@
private void endGestureStateOverride(boolean finishedToApp) {
mCallbacks.removeListener(this);
mTaskBarRecentsAnimationListener = null;
+ ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
// Update the resumed state immediately to ensure a seamless handoff
boolean launcherResumed = !finishedToApp;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 4ff0649..60f50a9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -18,7 +18,6 @@
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS;
@@ -28,11 +27,14 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -70,9 +72,8 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarNavButtonController:");
- pw.println(String.format(
- "%s\tmLastScreenPinLongPress=%dms", prefix, mLastScreenPinLongPress));
- pw.println(String.format("%s\tmScreenPinned=%b", prefix, mScreenPinned));
+ pw.println(prefix + "\tmLastScreenPinLongPress=" + mLastScreenPinLongPress);
+ pw.println(prefix + "\tmScreenPinned=" + mScreenPinned);
}
@Retention(RetentionPolicy.SOURCE)
@@ -113,7 +114,9 @@
mHandler = handler;
}
- public void onButtonClick(@TaskbarButton int buttonType) {
+ public void onButtonClick(@TaskbarButton int buttonType, View view) {
+ // Provide the same haptic feedback that the system offers for virtual keys.
+ view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
switch (buttonType) {
case BUTTON_BACK:
logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
@@ -144,7 +147,9 @@
}
}
- public boolean onButtonLongClick(@TaskbarButton int buttonType) {
+ public boolean onButtonLongClick(@TaskbarButton int buttonType, View view) {
+ // Provide the same haptic feedback that the system offers for virtual keys.
+ view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
switch (buttonType) {
case BUTTON_HOME:
logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 58ace17..c3b0f57 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -98,6 +98,6 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarScrimViewController:");
- pw.println(String.format("%s\tmScrimAlpha.value=%.2f", prefix, mScrimAlpha.value));
+ pw.println(prefix + "\tmScrimAlpha.value=" + mScrimAlpha.value);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index fc9f9d0..513f1bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
import static com.android.launcher3.taskbar.Utilities.appendFlag;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.animation.Animator;
@@ -30,13 +31,15 @@
import android.annotation.Nullable;
import android.content.SharedPreferences;
import android.util.Log;
+import android.view.View;
import android.view.ViewConfiguration;
-import android.view.WindowInsets;
+import androidx.annotation.NonNull;
+
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.AnimatedFloat;
@@ -44,6 +47,8 @@
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.IntPredicate;
@@ -53,6 +58,8 @@
*/
public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController {
+ private static final String TAG = "TaskbarStashController";
+
public static final int FLAG_IN_APP = 1 << 0;
public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
@@ -149,6 +156,7 @@
private @Nullable AnimatorSet mAnimator;
private boolean mIsSystemGestureInProgress;
private boolean mIsImeShowing;
+ private boolean mIsImeSwitcherShowing;
private boolean mEnableManualStashingForTests = false;
@@ -401,6 +409,7 @@
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
+ addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
if (!supportsVisualStashing()) {
// Just hide/show the icons and background instead of stashing into a handle.
@@ -496,6 +505,28 @@
});
}
+ private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
+ Optional<View> optionalView =
+ Arrays.stream(mControllers.taskbarViewController.getIconViews()).findFirst();
+ if (optionalView.isEmpty()) {
+ Log.wtf(TAG, "No views to start Interaction jank monitor with.", new Exception());
+ return;
+ }
+ View v = optionalView.get();
+ int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
+ InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ InteractionJankMonitor.getInstance().begin(v, action);
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ InteractionJankMonitor.getInstance().end(action);
+ }
+ });
+ }
/**
* Creates and starts a partial stash animation, hinting at the new state that will trigger when
* long press is detected.
@@ -571,9 +602,11 @@
}
// Only update the following flags when system gesture is not in progress.
- maybeResetStashedInAppAllApps(hasAnyFlag(FLAG_STASHED_IN_APP_IME) == mIsImeShowing);
- if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != mIsImeShowing) {
- updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+ boolean shouldStashForIme = shouldStashForIme();
+ maybeResetStashedInAppAllApps(
+ hasAnyFlag(FLAG_STASHED_IN_APP_IME) == shouldStashForIme);
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
+ updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme);
applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
}
}
@@ -625,8 +658,9 @@
// Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
+ mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
if (!mIsSystemGestureInProgress) {
- updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+ updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme());
animDuration = TASKBAR_STASH_DURATION_FOR_IME;
startDelay = getTaskbarStashStartDelayForIme();
}
@@ -634,6 +668,10 @@
applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
}
+ private boolean shouldStashForIme() {
+ return mIsImeShowing || mIsImeSwitcherShowing;
+ }
+
/**
* Updates the proper flag to indicate whether the task bar should be stashed.
*
@@ -690,15 +728,14 @@
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarStashController:");
- pw.println(String.format("%s\tmStashedHeight=%dpx", prefix, mStashedHeight));
- pw.println(String.format("%s\tmUnstashedHeight=%dpx", prefix, mUnstashedHeight));
- pw.println(String.format("%s\tmIsStashed=%b", prefix, mIsStashed));
- pw.println(String.format(
- "%s\tappliedState=%s", prefix, getStateString(mStatePropertyHolder.mPrevFlags)));
- pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
- pw.println(String.format(
- "%s\tmIsSystemGestureInProgress=%b", prefix, mIsSystemGestureInProgress));
- pw.println(String.format("%s\tmIsImeShowing=%b", prefix, mIsImeShowing));
+ pw.println(prefix + "\tmStashedHeight=" + mStashedHeight);
+ pw.println(prefix + "\tmUnstashedHeight=" + mUnstashedHeight);
+ pw.println(prefix + "\tmIsStashed=" + mIsStashed);
+ pw.println(prefix + "\tappliedState=" + getStateString(mStatePropertyHolder.mPrevFlags));
+ pw.println(prefix + "\tmState=" + getStateString(mState));
+ pw.println(prefix + "\tmIsSystemGestureInProgress=" + mIsSystemGestureInProgress);
+ pw.println(prefix + "\tmIsImeShowing=" + mIsImeShowing);
+ pw.println(prefix + "\tmIsImeSwitcherShowing=" + mIsImeSwitcherShowing);
}
private static String getStateString(int flags) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6f88d64..ea15acb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -20,6 +20,7 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,7 @@
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -40,7 +42,6 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.AllAppsButton;
@@ -78,6 +79,8 @@
// Only non-null when device supports having an All Apps button.
private @Nullable AllAppsButton mAllAppsButton;
+ private View mQsb;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -117,6 +120,9 @@
new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
}
+
+ // TODO: Disable touch events on QSB otherwise it can crash.
+ mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -166,6 +172,7 @@
if (mAllAppsButton != null) {
removeView(mAllAppsButton);
}
+ removeView(mQsb);
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -242,6 +249,11 @@
int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
addView(mAllAppsButton, index);
}
+ if (mActivityContext.getDeviceProfile().isQsbInline) {
+ addView(mQsb, Utilities.isRtl(getResources()) ? getChildCount() : 0);
+ // Always set QSB to invisible after re-adding.
+ mQsb.setVisibility(View.INVISIBLE);
+ }
mThemeIconsBackground = calculateThemeIconsBackground();
setThemedIconsBackgroundColor(mThemeIconsBackground);
@@ -273,8 +285,13 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
- int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
- int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
+ int countExcludingQsb = count;
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ if (deviceProfile.isQsbInline) {
+ countExcludingQsb--;
+ }
+ int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
int iconEnd = right - (right - left - spaceNeeded) / 2;
boolean needMoreSpaceForNav = layoutRtl ?
@@ -292,10 +309,25 @@
mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
for (int i = count; i > 0; i--) {
View child = getChildAt(i - 1);
- iconEnd -= mItemMarginLeftRight;
- int iconStart = iconEnd - mIconTouchSize;
- child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
- iconEnd = iconStart - mItemMarginLeftRight;
+ if (child == mQsb) {
+ int qsbStart;
+ int qsbEnd;
+ if (layoutRtl) {
+ qsbStart = iconEnd + mItemMarginLeftRight;
+ qsbEnd = qsbStart + deviceProfile.qsbWidth;
+ } else {
+ qsbEnd = iconEnd - mItemMarginLeftRight;
+ qsbStart = qsbEnd - deviceProfile.qsbWidth;
+ }
+ int qsbTop = (bottom - top - deviceProfile.hotseatQsbHeight) / 2;
+ int qsbBottom = qsbTop + deviceProfile.hotseatQsbHeight;
+ child.layout(qsbStart, qsbTop, qsbEnd, qsbBottom);
+ } else {
+ iconEnd -= mItemMarginLeftRight;
+ int iconStart = iconEnd - mIconTouchSize;
+ child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
+ iconEnd = iconStart - mItemMarginLeftRight;
+ }
}
mIconLayoutBounds.left = iconEnd;
}
@@ -367,6 +399,13 @@
return mAllAppsButton;
}
+ /**
+ * Returns the QSB in the taskbar.
+ */
+ public View getQsb() {
+ return mQsb;
+ }
+
// FolderIconParent implemented methods.
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 3562f5b..6d45fd2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -35,12 +36,15 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.HorizontalInsettableView;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiValueAlpha;
@@ -63,7 +67,9 @@
public static final int ALPHA_INDEX_STASH = 2;
public static final int ALPHA_INDEX_RECENTS_DISABLED = 3;
public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
- private static final int NUM_ALPHA_CHANNELS = 5;
+ public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5;
+ public static final int ALPHA_INDEX_IME_BUTTON_NAV = 6;
+ private static final int NUM_ALPHA_CHANNELS = 7;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
@@ -138,6 +144,14 @@
}
/**
+ * Should be called when the IME switcher visibility changes.
+ */
+ public void setIsImeSwitcherVisible(boolean isImeSwitcherVisible) {
+ mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
+ isImeSwitcherVisible ? 0 : 1);
+ }
+
+ /**
* Should be called when the recents button is disabled, so we can hide taskbar icons as well.
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
@@ -210,9 +224,10 @@
* 0 => not aligned
* 1 => fully aligned
*/
- public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+ public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment,
+ DeviceProfile launcherDp) {
if (mIconAlignControllerLazy == null) {
- mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
+ mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment);
}
mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
if (alignmentRatio <= 0 || alignmentRatio >= 1) {
@@ -224,11 +239,13 @@
/**
* Creates an animation for aligning the taskbar icons with the provided Launcher device profile
*/
- private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
+ private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp,
+ Float endAlignment) {
mOnControllerPreCreateCallback.run();
PendingAnimation setter = new PendingAnimation(100);
+ DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
- float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx;
+ float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.iconSizePx;
int borderSpacing = launcherDp.hotseatBorderSpace;
int hotseatCellSize = DeviceProfile.calculateCellWidth(
launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right,
@@ -245,14 +262,13 @@
}
int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
- int expandedHeight = Math.max(collapsedHeight,
- mActivity.getDeviceProfile().taskbarSize + offsetY);
+ int expandedHeight = Math.max(collapsedHeight, taskbarDp.taskbarSize + offsetY);
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
+ boolean isToHome = endAlignment != null && endAlignment == 1;
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
-
int positionInHotseat;
if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
&& child == mTaskbarView.getAllAppsButtonView()) {
@@ -260,13 +276,44 @@
// as its convenient for animation purposes.
positionInHotseat = Utilities.isRtl(child.getResources())
? -1
- : mActivity.getDeviceProfile().numShownHotseatIcons;
+ : taskbarDp.numShownHotseatIcons;
if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
- setter.setViewAlpha(child, 0, LINEAR);
+ setter.setViewAlpha(child, 0,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
+ : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
}
} else if (child.getTag() instanceof ItemInfo) {
positionInHotseat = ((ItemInfo) child.getTag()).screenId;
+ } else if (child == mTaskbarView.getQsb()) {
+ boolean isRtl = Utilities.isRtl(child.getResources());
+ float hotseatIconCenter = isRtl
+ ? launcherDp.widthPx - hotseatPadding.right + borderSpacing
+ + launcherDp.qsbWidth / 2f
+ : hotseatPadding.left - borderSpacing - launcherDp.qsbWidth / 2f;
+ float childCenter = (child.getLeft() + child.getRight()) / 2f;
+ float halfQsbIconWidthDiff = (launcherDp.qsbWidth - taskbarDp.iconSizePx) / 2f;
+ setter.addFloat(child, ICON_TRANSLATE_X,
+ isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
+ hotseatIconCenter - childCenter, LINEAR);
+
+ float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
+ setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+
+ setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
+ isToHome
+ ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
+ : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+ setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));
+
+ float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.qsbWidth;
+ if (child instanceof HorizontalInsettableView) {
+ setter.addFloat((HorizontalInsettableView) child,
+ HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
+ LINEAR);
+ }
+ continue;
} else {
Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
continue;
@@ -326,6 +373,19 @@
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarViewController:");
+
+ mTaskbarIconAlpha.dump(
+ prefix + "\t",
+ pw,
+ "mTaskbarIconAlpha",
+ "ALPHA_INDEX_HOME",
+ "ALPHA_INDEX_KEYGUARD",
+ "ALPHA_INDEX_STASH",
+ "ALPHA_INDEX_RECENTS_DISABLED",
+ "ALPHA_INDEX_NOTIFICATION_EXPANDED",
+ "ALPHA_INDEX_ASSISTANT_INVOKED",
+ "ALPHA_INDEX_IME_BUTTON_NAV");
+
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
new file mode 100644
index 0000000..946873e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -0,0 +1,109 @@
+package com.android.launcher3.taskbar
+
+import android.graphics.Canvas
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import com.android.launcher3.views.BaseDragLayer
+import com.android.systemui.animation.ViewRootSync
+import java.io.PrintWriter
+
+private const val TASKBAR_ICONS_FADE_DURATION = 300L
+private const val STASHED_HANDLE_FADE_DURATION = 180L
+
+/**
+ * Controls Taskbar behavior while Voice Interaction Window (assistant) is showing.
+ */
+class VoiceInteractionWindowController(val context: TaskbarActivityContext)
+ : TaskbarControllers.LoggableTaskbarController {
+
+ private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
+
+ // Initialized in init.
+ private lateinit var controllers: TaskbarControllers
+ private lateinit var separateWindowForTaskbarBackground: BaseDragLayer<TaskbarActivityContext>
+ private lateinit var separateWindowLayoutParams: WindowManager.LayoutParams
+
+ private var isVoiceInteractionWindowVisible: Boolean = false
+
+ fun init(controllers: TaskbarControllers) {
+ this.controllers = controllers
+
+ separateWindowForTaskbarBackground =
+ object : BaseDragLayer<TaskbarActivityContext>(context, null, 0) {
+ override fun recreateControllers() {
+ mControllers = emptyArray()
+ }
+
+ override fun draw(canvas: Canvas) {
+ super.draw(canvas)
+ taskbarBackgroundRenderer.draw(canvas)
+ }
+ }
+ separateWindowForTaskbarBackground.recreateControllers()
+ separateWindowForTaskbarBackground.setWillNotDraw(false)
+
+ separateWindowLayoutParams = context.createDefaultWindowLayoutParams(
+ TYPE_APPLICATION_OVERLAY)
+ separateWindowLayoutParams.isSystemApplicationOverlay = true
+ }
+
+ fun onDestroy() {
+ setIsVoiceInteractionWindowVisible(visible = false, skipAnim = true)
+ }
+
+ fun setIsVoiceInteractionWindowVisible(visible: Boolean, skipAnim: Boolean) {
+ if (isVoiceInteractionWindowVisible == visible) {
+ return
+ }
+ isVoiceInteractionWindowVisible = visible
+
+ // Fade out taskbar icons and stashed handle.
+ val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
+ val fadeTaskbarIcons = controllers.taskbarViewController.taskbarIconAlpha
+ .getProperty(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .animateToValue(taskbarIconAlpha)
+ .setDuration(TASKBAR_ICONS_FADE_DURATION)
+ val fadeStashedHandle = controllers.stashedHandleViewController.stashedHandleAlpha
+ .getProperty(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
+ .animateToValue(taskbarIconAlpha)
+ .setDuration(STASHED_HANDLE_FADE_DURATION)
+ fadeTaskbarIcons.start()
+ fadeStashedHandle.start()
+ if (skipAnim) {
+ fadeTaskbarIcons.end()
+ fadeStashedHandle.end()
+ }
+
+ if (context.isGestureNav && controllers.taskbarStashController.isInAppAndNotStashed) {
+ moveTaskbarBackgroundToLowerLayer()
+ }
+ }
+
+ /**
+ * Hides the TaskbarDragLayer background and creates a new window to draw just that background.
+ */
+ private fun moveTaskbarBackgroundToLowerLayer() {
+ val taskbarBackgroundOverride = controllers.taskbarDragLayerController
+ .overrideBackgroundAlpha
+ if (isVoiceInteractionWindowVisible) {
+ // First add the temporary window, then hide the overlapping taskbar background.
+ context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams)
+ ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
+ ) {
+ taskbarBackgroundOverride.updateValue(0f)
+ }
+ } else {
+ // First reapply the original taskbar background, then remove the temporary window.
+ taskbarBackgroundOverride.updateValue(1f)
+ ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
+ ) {
+ context.removeWindowView(separateWindowForTaskbarBackground)
+ }
+ }
+ }
+
+ override fun dumpLogs(prefix: String, pw: PrintWriter) {
+ pw.println(prefix + "VoiceInteractionWindowController:")
+ pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 6fd98db..6c43e50 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -171,8 +171,8 @@
* This method should be called after an exit animation finishes, if applicable.
*/
void maybeCloseWindow() {
- if (AbstractFloatingView.getOpenView(mAllAppsContext, TYPE_ALL) != null
- || mAllAppsContext.getDragController().isSystemDragInProgress()) {
+ if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
+ || mAllAppsContext.getDragController().isSystemDragInProgress())) {
return;
}
mProxyView.close(false);
@@ -187,7 +187,7 @@
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext)
.map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeView(mAllAppsContext.getDragLayer()));
+ .ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
mAllAppsContext = null;
}
@@ -207,7 +207,7 @@
private LayoutParams createLayoutParams() {
LayoutParams layoutParams = new LayoutParams(
TYPE_APPLICATION_OVERLAY,
- 0,
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(WINDOW_TITLE);
layoutParams.gravity = Gravity.BOTTOM;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 2f8e4d9..f450496 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,14 +17,9 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
-import android.content.Context;
import android.content.pm.ShortcutInfo;
-import android.content.res.Resources;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
public class ApiWrapper {
@@ -34,24 +29,4 @@
Person[] persons = si.getPersons();
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
}
-
- /**
- * Returns the minimum space that should be left empty at the end of hotseat
- */
- public static int getHotseatEndOffset(Context context) {
- if (DisplayController.getNavigationMode(context) == NavigationMode.THREE_BUTTONS) {
- Resources res = context.getResources();
- /*
- * 3 nav buttons +
- * Little space at the end for contextual buttons +
- * Little space between icons and nav buttons
- */
- return 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
- + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin)
- + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
- } else {
- return 0;
- }
-
- }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 84b3839..7166a3e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.OVERVIEW_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_SECONDARY_TRANSLATION;
@@ -74,6 +75,7 @@
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
RECENTS_GRID_PROGRESS.set(mRecentsView,
state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
+ OVERVIEW_PROGRESS.set(mRecentsView, state == LauncherState.OVERVIEW ? 1f : 0f);
}
@Override
@@ -117,6 +119,10 @@
boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile());
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
showAsGrid ? INSTANT : FINAL_FRAME);
+
+ boolean toOverview = toState == LauncherState.OVERVIEW;
+ setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, toOverview ? 1f : 0f,
+ toOverview ? INSTANT : FINAL_FRAME);
}
abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a74774c..9f2efc4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -31,8 +31,6 @@
*/
public class AllAppsState extends LauncherState {
- private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
-
private static final int STATE_FLAGS =
FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
@@ -60,7 +58,8 @@
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
+ return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
+ NO_OFFSET);
}
@Override
@@ -71,7 +70,7 @@
ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
.getWorkspaceScaleAndTranslation(launcher);
return new ScaleAndTranslation(
- WORKSPACE_SCALE_FACTOR,
+ launcher.getDeviceProfile().workspaceContentScale,
overviewScaleAndTranslation.translationX,
overviewScaleAndTranslation.translationY);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 6427e09..c4c9038 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -22,12 +22,10 @@
import android.graphics.Rect;
import android.os.SystemProperties;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.util.LayoutUtils;
@@ -95,13 +93,7 @@
@Override
public boolean isTaskbarStashed(Launcher launcher) {
- if (launcher instanceof BaseQuickstepLauncher) {
- LauncherTaskbarUIController uiController =
- ((BaseQuickstepLauncher) launcher).getTaskbarUIController();
-
- return uiController != null && uiController.supportsVisualStashing();
- }
- return super.isTaskbarStashed(launcher);
+ return true;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 2d7fe69..4d2f965 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -42,17 +42,10 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD;
-import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD;
-import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_OPAQUE_THRESHOLD;
-import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_VISIBLE_THRESHOLD;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import android.animation.ValueAnimator;
@@ -60,8 +53,8 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
@@ -182,23 +175,9 @@
}
config.duration = Math.max(config.duration, mHintToNormalDuration);
} else if (fromState == ALL_APPS && toState == NORMAL) {
- boolean isTablet = mActivity.getDeviceProfile().isTablet;
- config.setInterpolator(ANIM_ALL_APPS_FADE,
- isTablet ? FINAL_FRAME : Interpolators.clampToProgress(LINEAR,
- 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
- 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
- config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(LINEAR,
- 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
- 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
- if (!isTablet) {
- config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
- }
+ AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config);
} else if (fromState == NORMAL && toState == ALL_APPS) {
- if (mActivity.getDeviceProfile().isTablet) {
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
- }
- // TODO(b/231682175): centralize this setup in AllAppsSwipeController
+ AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 53dc9dd..f294986 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -232,6 +232,7 @@
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
(fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
+ mRecentsView.setTaskIconScaledDown(true);
float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
// As we drag right, animate the following properties:
@@ -321,6 +322,7 @@
boolean verticalFling = mSwipeDetector.isFling(velocity.y);
boolean noFling = !horizontalFling && !verticalFling;
if (mMotionPauseDetector.isPaused() && noFling) {
+ // Going to Overview.
cancelAnimations();
StateAnimationConfig config = new StateAnimationConfig();
@@ -331,6 +333,8 @@
@Override
public void onAnimationEnd(Animator animation) {
onAnimationToStateCompleted(OVERVIEW);
+ // Animate the icon after onAnimationToStateCompleted() so it doesn't clobber.
+ mRecentsView.animateUpTaskIconScale();
}
});
overviewAnim.start();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index e56c90c..9efbc34 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -21,21 +21,8 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-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.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_HOTSEAT_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
-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_SCALE;
import android.view.MotionEvent;
-import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -44,6 +31,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.quickstep.SystemUiProxy;
@@ -58,53 +46,6 @@
private static final String TAG = "PortraitStatesTouchCtrl";
- /**
- * The progress at which all apps content will be fully visible.
- */
- public static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
-
- /**
- * Minimum clamping progress for fading in all apps content
- */
- public static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
-
- /**
- * Minimum clamping progress for fading in all apps scrim
- */
- public static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
-
- /**
- * Maximum clamping progress for opaque all apps scrim
- */
- public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
-
- // Custom timing for NORMAL -> ALL_APPS on phones only.
- private static final float ALL_APPS_STATE_TRANSITION = 0.4f;
- private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f;
-
- // Custom interpolators for NORMAL -> ALL_APPS on phones only.
- private static final Interpolator LINEAR_EARLY =
- Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION);
- private static final Interpolator STEP_TRANSITION =
- Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION);
- // The blur to and from All Apps is set to be complete when the interpolator is at 0.5.
- public static final Interpolator BLUR =
- Interpolators.clampToProgress(
- Interpolators.mapToProgress(LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS),
- 0f, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator WORKSPACE_FADE = STEP_TRANSITION;
- public static final Interpolator WORKSPACE_SCALE = LINEAR_EARLY;
- public static final Interpolator HOTSEAT_FADE = STEP_TRANSITION;
- public static final Interpolator HOTSEAT_SCALE = LINEAR_EARLY;
- public static final Interpolator HOTSEAT_TRANSLATE = STEP_TRANSITION;
- public static final Interpolator SCRIM_FADE = LINEAR_EARLY;
- public static final Interpolator ALL_APPS_FADE =
- Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1f);
- public static final Interpolator ALL_APPS_VERTICAL_PROGRESS =
- Interpolators.clampToProgress(
- Interpolators.mapToProgress(LINEAR, ALL_APPS_STATE_TRANSITION, 1f),
- ALL_APPS_STATE_TRANSITION, 1f);
-
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
public PortraitStatesTouchController(Launcher l) {
@@ -160,66 +101,15 @@
return fromState;
}
- private StateAnimationConfig getNormalToAllAppsAnimation() {
- StateAnimationConfig builder = new StateAnimationConfig();
- if (mLauncher.getDeviceProfile().isTablet) {
- builder.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
- builder.setInterpolator(ANIM_SCRIM_FADE,
- Interpolators.clampToProgress(LINEAR,
- ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
- ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
- } else {
- // TODO(b/231682175): centralize this setup in AllAppsSwipeController.
- builder.setInterpolator(ANIM_DEPTH, BLUR);
- builder.setInterpolator(ANIM_WORKSPACE_FADE, WORKSPACE_FADE);
- builder.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE);
- builder.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE);
- builder.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE);
- builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE);
- builder.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE);
- builder.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE);
- builder.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS);
- }
- return builder;
- }
-
- private StateAnimationConfig getAllAppsToNormalAnimation() {
- StateAnimationConfig builder = new StateAnimationConfig();
- if (mLauncher.getDeviceProfile().isTablet) {
- builder.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
- builder.setInterpolator(ANIM_SCRIM_FADE,
- Interpolators.clampToProgress(LINEAR,
- 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
- 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
- } else {
- // These interpolators are the reverse of the ones used above, so swiping out of All
- // Apps feels the same as swiping into it.
- // TODO(b/231682175): centralize this setup in AllAppsSwipeController.
- builder.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR));
- builder.setInterpolator(ANIM_WORKSPACE_FADE, Interpolators.reverse(WORKSPACE_FADE));
- builder.setInterpolator(ANIM_WORKSPACE_SCALE, Interpolators.reverse(WORKSPACE_SCALE));
- builder.setInterpolator(ANIM_HOTSEAT_FADE, Interpolators.reverse(HOTSEAT_FADE));
- builder.setInterpolator(ANIM_HOTSEAT_SCALE, Interpolators.reverse(HOTSEAT_SCALE));
- builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE,
- Interpolators.reverse(HOTSEAT_TRANSLATE));
- builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE));
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.reverse(ALL_APPS_FADE));
- builder.setInterpolator(ANIM_VERTICAL_PROGRESS,
- Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS));
- }
- return builder;
- }
-
@Override
protected StateAnimationConfig getConfigForStates(
LauncherState fromState, LauncherState toState) {
- final StateAnimationConfig config;
+ final StateAnimationConfig config = new StateAnimationConfig();
+ config.userControlled = true;
if (fromState == NORMAL && toState == ALL_APPS) {
- config = getNormalToAllAppsAnimation();
+ AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mLauncher, config);
} else if (fromState == ALL_APPS && toState == NORMAL) {
- config = getAllAppsToNormalAnimation();
- } else {
- config = new StateAnimationConfig();
+ AllAppsSwipeController.applyAllAppsToNormalConfig(mLauncher, config);
}
return config;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 76f7718..8f1872b 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -58,6 +58,7 @@
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
@@ -140,7 +141,7 @@
RecentsAnimationCallbacks.RecentsAnimationListener {
private static final String TAG = "AbsSwipeUpHandler";
- private static final String[] STATE_NAMES = DEBUG_STATES ? new String[17] : null;
+ private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
@@ -171,66 +172,68 @@
}
};
- private static int getFlagForIndex(int index, String name) {
+ private static int FLAG_COUNT = 0;
+ private static int getNextStateFlag(String name) {
if (DEBUG_STATES) {
- STATE_NAMES[index] = name;
+ STATE_NAMES.add(name);
}
- return 1 << index;
+ int index = 1 << FLAG_COUNT;
+ FLAG_COUNT++;
+ return index;
}
// Launcher UI related states
protected static final int STATE_LAUNCHER_PRESENT =
- getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
+ getNextStateFlag("STATE_LAUNCHER_PRESENT");
protected static final int STATE_LAUNCHER_STARTED =
- getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
+ getNextStateFlag("STATE_LAUNCHER_STARTED");
protected static final int STATE_LAUNCHER_DRAWN =
- getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+ getNextStateFlag("STATE_LAUNCHER_DRAWN");
// Called when the Launcher has connected to the touch interaction service (and the taskbar
// ui controller is initialized)
protected static final int STATE_LAUNCHER_BIND_TO_SERVICE =
- getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE");
+ getNextStateFlag("STATE_LAUNCHER_BIND_TO_SERVICE");
// Internal initialization states
private static final int STATE_APP_CONTROLLER_RECEIVED =
- getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
+ getNextStateFlag("STATE_APP_CONTROLLER_RECEIVED");
// Interaction finish states
private static final int STATE_SCALED_CONTROLLER_HOME =
- getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
+ getNextStateFlag("STATE_SCALED_CONTROLLER_HOME");
private static final int STATE_SCALED_CONTROLLER_RECENTS =
- getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
+ getNextStateFlag("STATE_SCALED_CONTROLLER_RECENTS");
protected static final int STATE_HANDLER_INVALIDATED =
- getFlagForIndex(7, "STATE_HANDLER_INVALIDATED");
+ getNextStateFlag("STATE_HANDLER_INVALIDATED");
private static final int STATE_GESTURE_STARTED =
- getFlagForIndex(8, "STATE_GESTURE_STARTED");
+ getNextStateFlag("STATE_GESTURE_STARTED");
private static final int STATE_GESTURE_CANCELLED =
- getFlagForIndex(9, "STATE_GESTURE_CANCELLED");
+ getNextStateFlag("STATE_GESTURE_CANCELLED");
private static final int STATE_GESTURE_COMPLETED =
- getFlagForIndex(10, "STATE_GESTURE_COMPLETED");
+ getNextStateFlag("STATE_GESTURE_COMPLETED");
private static final int STATE_CAPTURE_SCREENSHOT =
- getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT");
+ getNextStateFlag("STATE_CAPTURE_SCREENSHOT");
protected static final int STATE_SCREENSHOT_CAPTURED =
- getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED");
+ getNextStateFlag("STATE_SCREENSHOT_CAPTURED");
private static final int STATE_SCREENSHOT_VIEW_SHOWN =
- getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN");
+ getNextStateFlag("STATE_SCREENSHOT_VIEW_SHOWN");
private static final int STATE_RESUME_LAST_TASK =
- getFlagForIndex(14, "STATE_RESUME_LAST_TASK");
+ getNextStateFlag("STATE_RESUME_LAST_TASK");
private static final int STATE_START_NEW_TASK =
- getFlagForIndex(15, "STATE_START_NEW_TASK");
+ getNextStateFlag("STATE_START_NEW_TASK");
private static final int STATE_CURRENT_TASK_FINISHED =
- getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED");
+ getNextStateFlag("STATE_CURRENT_TASK_FINISHED");
private static final int STATE_FINISH_WITH_NO_END =
- getFlagForIndex(17, "STATE_FINISH_WITH_NO_END");
+ getNextStateFlag("STATE_FINISH_WITH_NO_END");
private static final int LAUNCHER_UI_STATES =
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
STATE_LAUNCHER_BIND_TO_SERVICE;
public static final long MAX_SWIPE_DURATION = 350;
- 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 =
@@ -298,9 +301,12 @@
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumerProxy =
- new InputConsumerProxy(context,
- () -> mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation(),
- inputConsumer, () -> {
+ new InputConsumerProxy(context, /* rotationSupplier = */ () -> {
+ if (mRecentsView == null) {
+ return ROTATION_0;
+ }
+ return mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation();
+ }, inputConsumer, /* callback = */ () -> {
endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
endLauncherTransitionController();
}, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
@@ -315,7 +321,7 @@
}
private void initStateCallbacks() {
- mStateCallback = new MultiStateCallback(STATE_NAMES);
+ mStateCallback = new MultiStateCallback(STATE_NAMES.toArray(new String[0]));
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
this::onLauncherPresentAndGestureStarted);
@@ -442,7 +448,7 @@
});
setupRecentsViewUi();
- linkRecentsViewScroll();
+ mRecentsView.runOnPageScrollsInitialized(this::linkRecentsViewScroll);
activity.runOnBindToTouchInteractionService(this::onLauncherBindToService);
mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
@@ -571,7 +577,7 @@
protected void notifyGestureAnimationStartToRecents() {
Task[] runningTasks;
- if (mIsSwipeForStagedSplit) {
+ if (mIsSwipeForSplit) {
int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds);
} else {
@@ -862,6 +868,7 @@
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
if (mRecentsView != null) {
+ final View rv = mRecentsView;
mRecentsView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
boolean mHandled = false;
@@ -877,8 +884,7 @@
InteractionJankMonitorWrapper.begin(mRecentsView,
InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
- mRecentsView.post(() ->
- mRecentsView.getViewTreeObserver().removeOnDrawListener(this));
+ rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
}
});
}
@@ -1137,7 +1143,9 @@
mInputConsumerProxy.enable();
}
if (endTarget == HOME) {
- duration = HOME_DURATION;
+ duration = mActivity != null && mActivity.getDeviceProfile().isTaskbarPresent
+ ? StaggeredWorkspaceAnim.DURATION_TASKBAR_MS
+ : StaggeredWorkspaceAnim.DURATION_MS;
// Early detach the nav bar once the endTarget is determined as HOME
if (mRecentsAnimationController != null) {
mRecentsAnimationController.detachNavigationBarFromApp(true);
@@ -1280,15 +1288,18 @@
? runningTaskTarget.taskInfo.launchCookies
: new ArrayList<>();
boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
+ boolean hasValidLeash = runningTaskTarget != null
+ && runningTaskTarget.leash != null
+ && runningTaskTarget.leash.isValid();
boolean appCanEnterPip = !mDeviceState.isPipActive()
- && runningTaskTarget != null
+ && hasValidLeash
&& runningTaskTarget.allowEnterPip
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
&& runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
HomeAnimationFactory homeAnimFactory =
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
- mIsSwipingPipToHome = !mIsSwipeForStagedSplit && appCanEnterPip;
+ mIsSwipingPipToHome = !mIsSwipeForSplit && appCanEnterPip;
final RectFSpringAnim[] windowAnim;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = createWindowAnimationToPip(
@@ -1390,9 +1401,6 @@
}
}
- /**
- * TODO(b/195473090) handle multiple task simulators (if needed) for PIP
- */
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
@@ -1417,6 +1425,16 @@
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
mDp.hotseatBarSizePx);
+ final Rect appBounds = new Rect();
+ final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
+ // Adjust the appBounds for TaskBar by using the calculated window crop Rect
+ // from TaskViewSimulator and fallback to the bounds in TaskInfo when it's originated
+ // from windowing modes other than full-screen.
+ if (winConfig.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
+ mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCropRect().round(appBounds);
+ } else {
+ appBounds.set(winConfig.getBounds());
+ }
final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder()
.setContext(mContext)
.setTaskId(runningTaskTarget.taskId)
@@ -1424,7 +1442,7 @@
.setLeash(runningTaskTarget.leash)
.setSourceRectHint(
runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint())
- .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds())
+ .setAppBounds(appBounds)
.setHomeToWindowPositionMap(homeToWindowPositionMap)
.setStartBounds(startRect)
.setDestinationBounds(destinationBounds)
@@ -1611,7 +1629,8 @@
// Cleanup when switching handlers
mInputConsumerProxy.unregisterCallback();
mActivityInitListener.unregister();
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mActivityRestartListener);
mTaskSnapshot = null;
}
@@ -1675,7 +1694,7 @@
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
- if (mRecentsAnimationTargets != null) {
+ if (mRecentsAnimationTargets != null && wasVisible) {
setDividerShown(true /* shown */, true /* immediate */);
}
@@ -1792,12 +1811,13 @@
mSwipePipToHomeAnimator.getFinishTransaction(),
mSwipePipToHomeAnimator.getContentOverlay());
mIsSwipingPipToHome = false;
- } else if (mIsSwipeForStagedSplit) {
+ } else if (mIsSwipeForSplit) {
// Transaction to hide the task to avoid flicker for entering PiP from split-screen.
PictureInPictureSurfaceTransaction tx =
new PictureInPictureSurfaceTransaction.Builder()
.setAlpha(0f)
.build();
+ tx.setShouldDisableCanAffectSystemUiFlags(false);
int[] taskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
for (int taskId : taskIds) {
mRecentsAnimationController.setFinishTaskTransaction(taskId,
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 6c7a885..a166553 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -133,4 +133,11 @@
public boolean isAnimatingToValue(float endValue) {
return isAnimating() && mEndValue != null && mEndValue == endValue;
}
+
+ /**
+ * Returns the value we are animating to, or {@code null} if we are not currently animating.
+ */
+ public Float getEndValue() {
+ return mEndValue;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 2fcd286..6354282 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -56,13 +56,10 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
-import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
-import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -258,7 +255,7 @@
private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
Rect potentialTaskRect, float maxScale, int gravity, Rect outRect) {
- PointF taskDimension = getTaskDimension(context, dp);
+ PointF taskDimension = getTaskDimension(dp);
float scale = Math.min(
potentialTaskRect.width() / taskDimension.x,
@@ -270,47 +267,20 @@
Gravity.apply(gravity, outWidth, outHeight, potentialTaskRect, outRect);
}
- private static PointF getTaskDimension(Context context, DeviceProfile dp) {
+ private static PointF getTaskDimension(DeviceProfile dp) {
PointF dimension = new PointF();
- getTaskDimension(context, dp, dimension);
+ getTaskDimension(dp, dimension);
return dimension;
}
/**
* Gets the dimension of the task in the current system state.
*/
- public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) {
- if (dp.isMultiWindowMode) {
- WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
- out.x = bounds.availableSize.x;
- out.y = bounds.availableSize.y;
- if (!TaskView.clipLeft(dp)) {
- out.x += bounds.insets.left;
- }
- if (!TaskView.clipRight(dp)) {
- out.x += bounds.insets.right;
- }
- if (!TaskView.clipTop(dp)) {
- out.y += bounds.insets.top;
- }
- if (!TaskView.clipBottom(dp)) {
- out.y += bounds.insets.bottom;
- }
- } else {
- out.x = dp.widthPx;
- out.y = dp.heightPx;
- if (TaskView.clipLeft(dp)) {
- out.x -= dp.getInsets().left;
- }
- if (TaskView.clipRight(dp)) {
- out.x -= dp.getInsets().right;
- }
- if (TaskView.clipTop(dp)) {
- out.y -= dp.getInsets().top;
- }
- if (TaskView.clipBottom(dp)) {
- out.y -= Math.max(dp.getInsets().bottom, dp.taskbarSize);
- }
+ public static void getTaskDimension(DeviceProfile dp, PointF out) {
+ out.x = dp.widthPx;
+ out.y = dp.heightPx;
+ if (dp.isTablet) {
+ out.y -= dp.taskbarSize;
}
}
@@ -341,7 +311,7 @@
(taskRect.height() + dp.overviewTaskThumbnailTopMarginPx - dp.overviewRowSpacing)
/ 2f;
- PointF taskDimension = getTaskDimension(context, dp);
+ PointF taskDimension = getTaskDimension(dp);
float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / taskDimension.y;
int outWidth = Math.round(scale * taskDimension.x);
int outHeight = Math.round(scale * taskDimension.y);
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index ee5bb44..99f7bdd 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -23,16 +23,15 @@
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK;
import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
@@ -161,11 +160,7 @@
if (gestureContractAnimationFactory != null && runningTaskTarget != null) {
gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo);
}
- try {
- mContext.startActivity(intent, options.toBundle());
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- mContext.startActivity(createHomeIntent());
- }
+ startHomeIntentSafely(mContext, intent, options.toBundle());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 3b52e91..acdbbbd 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -38,6 +38,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -82,11 +83,11 @@
private static final String TAG = "GestureState";
- private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
+ private static final List<String> STATE_NAMES = new ArrayList<>();
public static final GestureState DEFAULT_STATE = new GestureState();
private static int FLAG_COUNT = 0;
- private static int getFlagForIndex(String name) {
+ private static int getNextStateFlag(String name) {
if (DEBUG_STATES) {
STATE_NAMES.add(name);
}
@@ -97,36 +98,36 @@
// Called when the end target as been set
public static final int STATE_END_TARGET_SET =
- getFlagForIndex("STATE_END_TARGET_SET");
+ getNextStateFlag("STATE_END_TARGET_SET");
// Called when the end target animation has finished
public static final int STATE_END_TARGET_ANIMATION_FINISHED =
- getFlagForIndex("STATE_END_TARGET_ANIMATION_FINISHED");
+ getNextStateFlag("STATE_END_TARGET_ANIMATION_FINISHED");
// Called when the recents animation has been requested to start
public static final int STATE_RECENTS_ANIMATION_INITIALIZED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_INITIALIZED");
+ getNextStateFlag("STATE_RECENTS_ANIMATION_INITIALIZED");
// Called when the recents animation is started and the TaskAnimationManager has been updated
// with the controller and targets
public static final int STATE_RECENTS_ANIMATION_STARTED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_STARTED");
+ getNextStateFlag("STATE_RECENTS_ANIMATION_STARTED");
// Called when the recents animation is canceled
public static final int STATE_RECENTS_ANIMATION_CANCELED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_CANCELED");
+ getNextStateFlag("STATE_RECENTS_ANIMATION_CANCELED");
// Called when the recents animation finishes
public static final int STATE_RECENTS_ANIMATION_FINISHED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_FINISHED");
+ getNextStateFlag("STATE_RECENTS_ANIMATION_FINISHED");
// Always called when the recents animation ends (regardless of cancel or finish)
public static final int STATE_RECENTS_ANIMATION_ENDED =
- getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
+ getNextStateFlag("STATE_RECENTS_ANIMATION_ENDED");
// Called when RecentsView stops scrolling and settles on a TaskView.
public static final int STATE_RECENTS_SCROLLING_FINISHED =
- getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
+ getNextStateFlag("STATE_RECENTS_SCROLLING_FINISHED");
// Needed to interact with the current activity
private final Intent mHomeIntent;
diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
index 154848d..2273806 100644
--- a/quickstep/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -78,16 +78,17 @@
addImageAndSendIntent(crop, intent, true, exceptionCallback);
}
- @UiThread
private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData,
@Nullable Runnable exceptionCallback) {
- if (mBitmapSupplier.get() == null) {
- Log.e(TAG, "No snapshot available, not starting share.");
- return;
- }
- UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
- mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ Bitmap bitmap = mBitmapSupplier.get();
+ if (bitmap == null) {
+ Log.e(TAG, "No snapshot available, not starting share.");
+ return;
+ }
+ persistBitmapAndStartActivity(mContext,
+ bitmap, crop, intent, (uri, intentForUri) -> {
intentForUri.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
if (setData) {
intentForUri.setData(uri);
@@ -95,7 +96,8 @@
intentForUri.putExtra(EXTRA_STREAM, uri);
}
return new Intent[]{intentForUri};
- }, TAG, exceptionCallback));
+ }, TAG, exceptionCallback);
+ });
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 50d1244..196a664 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -91,7 +91,7 @@
mActivity.setHintUserWillBeActive();
}
- if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForStagedSplit) {
+ if (!canUseWorkspaceView || appCanEnterPip || mIsSwipeForSplit) {
return new LauncherHomeAnimationFactory();
}
if (workspaceView instanceof LauncherAppWidgetHostView) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index dffdc5a..1e7e89e 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -235,6 +235,10 @@
}
};
+ RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView();
+ if (visibleRecentsView != null) {
+ visibleRecentsView.moveFocusedTaskToFront();
+ }
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
cmd.mActiveCallbacks.addListener(interactionHandler);
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0efe666..9e3173c 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,11 +20,11 @@
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -33,8 +33,12 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -276,4 +280,34 @@
overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
}
+
+ /**
+ * Starts the intent for the current home activity.
+ */
+ public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) {
+ RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context);
+ OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState);
+ Intent intent = observer.getHomeIntent();
+ observer.onDestroy();
+ deviceState.destroy();
+ startHomeIntentSafely(context, intent, options);
+ }
+
+ /**
+ * Starts the intent for the current home activity.
+ */
+ public static void startHomeIntentSafely(
+ @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options) {
+ try {
+ context.startActivity(homeIntent, options);
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ context.startActivity(createHomeIntent(), options);
+ }
+ }
+
+ private static Intent createHomeIntent() {
+ return new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 097850f..cd93dbe 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -34,7 +34,7 @@
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.StagedSplitBounds;
+import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -195,19 +195,19 @@
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
task2.setLastSnapshotData(taskInfo2);
}
- final SplitConfigurationOptions.StagedSplitBounds launcherSplitBounds =
- convertSplitBounds(rawTask.mStagedSplitBounds);
+ final SplitConfigurationOptions.SplitBounds launcherSplitBounds =
+ convertSplitBounds(rawTask.mSplitBounds);
allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
}
return allTasks;
}
- private SplitConfigurationOptions.StagedSplitBounds convertSplitBounds(
- StagedSplitBounds shellSplitBounds) {
+ private SplitConfigurationOptions.SplitBounds convertSplitBounds(
+ SplitBounds shellSplitBounds) {
return shellSplitBounds == null ?
null :
- new SplitConfigurationOptions.StagedSplitBounds(
+ new SplitConfigurationOptions.SplitBounds(
shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds,
shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 4f0b976..67ce606 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -21,11 +21,11 @@
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -428,7 +428,7 @@
RemoteAnimationAdapterCompat adapterCompat =
new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
getIApplicationThread());
- startActivity(createHomeIntent(),
+ startHomeIntentSafely(this,
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 2007ee1..fefef2f 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -104,8 +104,6 @@
}
if (mSplitScreenMinimized != splitScreenMinimized) {
mSplitScreenMinimized = splitScreenMinimized;
- UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(context)
- .setSplitScreenMinimized(splitScreenMinimized));
}
}
@@ -222,7 +220,6 @@
*/
public void enableInputConsumer() {
UI_HELPER_EXECUTOR.submit(() -> {
- mController.hideCurrentInputMethod();
mController.setInputConsumerEnabled(true);
});
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1634c08..442578e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -36,6 +36,7 @@
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -54,7 +55,8 @@
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel implements IconChangeListener, TaskStackChangeListener {
+public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
+ TaskVisualsChangeListener {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -77,6 +79,7 @@
IconProvider iconProvider = new IconProvider(context);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
+ mIconCache.registerTaskVisualsChangeListener(this);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
@@ -204,6 +207,13 @@
}
@Override
+ public void onTaskIconChanged(int taskId) {
+ for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
+ listener.onTaskIconChanged(taskId);
+ }
+ }
+
+ @Override
public void onSystemIconStateChanged(String iconState) {
mIconCache.clearCache();
}
@@ -226,20 +236,4 @@
writer.println(prefix + "RecentsModel:");
mTaskList.dump(" ", writer);
}
-
- /**
- * Listener for receiving various task properties changes
- */
- public interface TaskVisualsChangeListener {
-
- /**
- * Called whn the task thumbnail changes
- */
- Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
-
- /**
- * Called when the icon for a task changes
- */
- void onTaskIconChanged(String pkg, UserHandle user);
- }
}
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index b20d488..1bd808d 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -17,8 +17,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -114,10 +112,6 @@
}
public void release() {
- if (ENABLE_SHELL_TRANSITIONS) {
- mReleaseChecks.clear();
- return;
- }
if (mReleased) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index c3ea256..7183c49 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -20,7 +20,7 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -34,7 +34,7 @@
*/
public class RemoteTargetGluer {
private RemoteTargetHandle[] mRemoteTargetHandles;
- private StagedSplitBounds mStagedSplitBounds;
+ private SplitBounds mSplitBounds;
/**
* Use this constructor if remote targets are split-screen independent
@@ -118,18 +118,18 @@
// remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
// vice versa
- mStagedSplitBounds = new StagedSplitBounds(
+ mSplitBounds = new SplitBounds(
topLeftTarget.startScreenSpaceBounds,
bottomRightTarget.startScreenSpaceBounds, splitIds[0], splitIds[1]);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
- mStagedSplitBounds);
+ mSplitBounds);
mRemoteTargetHandles[1].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(targets, topLeftTarget));
mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget,
- mStagedSplitBounds);
+ mSplitBounds);
}
return mRemoteTargetHandles;
}
@@ -173,8 +173,8 @@
return mRemoteTargetHandles;
}
- public StagedSplitBounds getStagedSplitBounds() {
- return mStagedSplitBounds;
+ public SplitBounds getSplitBounds() {
+ return mSplitBounds;
}
/**
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 9667108..baeb514 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
import android.animation.Animator;
import android.content.Context;
@@ -72,16 +71,14 @@
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
- protected boolean mIsSwipeForStagedSplit;
+ protected boolean mIsSwipeForSplit;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
-
- mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() &&
- TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds().length > 1;
+ mIsSwipeForSplit = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds().length > 1;
mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getActivityInterface());
mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 39d8b54..9a2619b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -270,18 +270,6 @@
}
}
- @Override
- public Rect getNonMinimizedSplitScreenSecondaryBounds() {
- if (mSystemUiProxy != null) {
- try {
- return mSystemUiProxy.getNonMinimizedSplitScreenSecondaryBounds();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call getNonMinimizedSplitScreenSecondaryBounds", e);
- }
- }
- return null;
- }
-
public float getLastNavButtonAlpha() {
return mLastNavButtonAlpha;
}
@@ -385,28 +373,6 @@
}
@Override
- public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.handleImageAsScreenshot(bitmap, rect, insets, i);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call handleImageAsScreenshot", e);
- }
- }
- }
-
- @Override
- public void setSplitScreenMinimized(boolean minimized) {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.setSplitScreenMinimized(minimized);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setSplitScreenMinimized", e);
- }
- }
- }
-
- @Override
public void notifySwipeUpGestureStarted() {
if (mSystemUiProxy != null) {
try {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 54f457d..fd7e367 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -195,7 +195,10 @@
} else if (nonAppTargets != null && nonAppTargets.length > 0) {
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
- true /*shown*/, dividerAnimator -> dividerAnimator.start());
+ true /*shown*/, dividerAnimator -> {
+ dividerAnimator.start();
+ dividerAnimator.end();
+ });
}
if (mController != null) {
if (mLastAppearedTaskTarget == null
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 3803f03..dc60875 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
@@ -46,6 +47,7 @@
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
+import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -70,6 +72,9 @@
private BaseIconFactory mIconFactory;
+ @Nullable
+ public TaskVisualsChangeListener mTaskVisualsChangeListener = null;
+
public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
mContext = context;
mBgExecutor = bgExecutor;
@@ -116,6 +121,7 @@
task.icon = result.icon;
task.titleDescription = result.contentDescription;
callback.accept(task);
+ dispatchIconUpdate(task.key.id);
}
};
mBgExecutor.execute(request);
@@ -257,7 +263,8 @@
if (mIconFactory == null) {
mIconFactory = new BaseIconFactory(mContext,
DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(),
- mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.task_icon_cache_default_icon_size));
}
return mIconFactory;
}
@@ -272,4 +279,18 @@
public Drawable icon;
public String contentDescription = "";
}
+
+ void registerTaskVisualsChangeListener(TaskVisualsChangeListener newListener) {
+ mTaskVisualsChangeListener = newListener;
+ }
+
+ void removeTaskVisualsChangeListener() {
+ mTaskVisualsChangeListener = null;
+ }
+
+ void dispatchIconUpdate(int taskId) {
+ if (mTaskVisualsChangeListener != null) {
+ mTaskVisualsChangeListener.onTaskIconChanged(taskId);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 3ef1332..e10cab6 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -23,7 +23,6 @@
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint;
-import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -37,17 +36,12 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.ResourceBasedOverride;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.views.ActivityContext;
-import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -66,7 +60,7 @@
public class TaskOverlayFactory implements ResourceBasedOverride {
public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
- DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) {
+ TaskIdAttributeContainer taskContainer) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1;
@@ -75,17 +69,11 @@
continue;
}
- SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer);
- if (shortcut == null) {
+ List<SystemShortcut> menuShortcuts = menuOption.getShortcuts(activity, taskContainer);
+ if (menuShortcuts == null) {
continue;
}
-
- if (menuOption == TaskShortcutFactory.SPLIT_SCREEN &&
- FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
- addSplitOptions(shortcuts, activity, taskView, deviceProfile);
- } else {
- shortcuts.add(shortcut);
- }
+ shortcuts.addAll(menuShortcuts);
}
RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
boolean canLauncherRotate = orientedState.isRecentsActivityRotationAllowed();
@@ -94,61 +82,24 @@
// Add overview actions to the menu when in in-place rotate landscape mode.
if (!canLauncherRotate && isInLandscape) {
// Add screenshot action to task menu.
- SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
- .getShortcut(activity, taskContainer);
- if (screenshotShortcut != null) {
- shortcuts.add(screenshotShortcut);
+ List<SystemShortcut> screenshotShortcuts = TaskShortcutFactory.SCREENSHOT
+ .getShortcuts(activity, taskContainer);
+ if (screenshotShortcuts != null) {
+ shortcuts.addAll(screenshotShortcuts);
}
// Add modal action only if display orientation is the same as the device orientation.
if (orientedState.getDisplayRotation() == ROTATION_0) {
- SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
- .getShortcut(activity, taskContainer);
- if (modalShortcut != null) {
- shortcuts.add(modalShortcut);
+ List<SystemShortcut> modalShortcuts = TaskShortcutFactory.MODAL
+ .getShortcuts(activity, taskContainer);
+ if (modalShortcuts != null) {
+ shortcuts.addAll(modalShortcuts);
}
}
}
return shortcuts;
}
-
- /**
- * Does NOT add split options in the following scenarios:
- * * The taskView to add split options is already showing split screen tasks
- * * There aren't at least 2 tasks in overview to show split options for
- * * Device is in "Lock task mode"
- * * The taskView to show split options for is the focused task AND we haven't started
- * scrolling in overview (if we haven't scrolled, there's a split overview action button so
- * we don't need this menu option)
- */
- private static void addSplitOptions(List<SystemShortcut> outShortcuts,
- BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
- RecentsView recentsView = taskView.getRecentsView();
- PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
- int[] taskViewTaskIds = taskView.getTaskIds();
- boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
- taskViewTaskIds[1] != -1;
- boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2;
- boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
- boolean isTaskInExpectedScrollPosition =
- recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
- ActivityManager activityManager =
- (ActivityManager) taskView.getContext().getSystemService(Context.ACTIVITY_SERVICE);
- boolean isLockTaskMode = activityManager.isInLockTaskMode();
-
- if (taskViewHasMultipleTasks || notEnoughTasksToSplit || isLockTaskMode ||
- (isFocusedTask && isTaskInExpectedScrollPosition)) {
- return;
- }
-
- List<SplitPositionOption> positions =
- orientationHandler.getSplitPositionOptions(deviceProfile);
- for (SplitPositionOption option : positions) {
- outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option));
- }
- }
-
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay(thumbnailView);
}
@@ -170,7 +121,7 @@
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.APP_INFO,
- TaskShortcutFactory.SPLIT_SCREEN,
+ TaskShortcutFactory.SPLIT_SELECT,
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index e807e26..69a295b 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -16,11 +16,8 @@
package com.android.quickstep;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -30,8 +27,11 @@
import android.os.Handler;
import android.os.Looper;
import android.view.View;
+import android.view.WindowInsets;
import android.window.SplashScreen;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -39,6 +39,7 @@
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.quickstep.views.RecentsView;
@@ -55,21 +56,35 @@
import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
/**
* Represents a system shortcut that can be shown for a recent task.
*/
public interface TaskShortcutFactory {
- SystemShortcut getShortcut(BaseDraggingActivity activity,
- TaskIdAttributeContainer taskContainer);
+ @Nullable
+ default List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ return null;
+ }
default boolean showForSplitscreen() {
return false;
}
+ /** @return a singleton list if the provided shortcut is non-null, null otherwise */
+ @Nullable
+ default List<SystemShortcut> createSingletonShortcutList(@Nullable SystemShortcut shortcut) {
+ if (shortcut != null) {
+ return Collections.singletonList(shortcut);
+ }
+ return null;
+ }
+
TaskShortcutFactory APP_INFO = new TaskShortcutFactory() {
@Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity,
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
TaskIdAttributeContainer taskContainer) {
TaskView taskView = taskContainer.getTaskView();
AppInfo.SplitAccessibilityInfo accessibilityInfo =
@@ -77,7 +92,8 @@
TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
taskContainer.getA11yNodeId()
);
- return new AppInfo(activity, taskContainer.getItemInfo(), taskView, accessibilityInfo);
+ return Collections.singletonList(new AppInfo(activity, taskContainer.getItemInfo(),
+ taskView, accessibilityInfo));
}
@Override
@@ -86,40 +102,10 @@
}
};
- abstract class MultiWindowFactory implements TaskShortcutFactory {
-
- private final int mIconRes;
- private final int mTextRes;
- private final LauncherEvent mLauncherEvent;
-
- MultiWindowFactory(int iconRes, int textRes, LauncherEvent launcherEvent) {
- mIconRes = iconRes;
- mTextRes = textRes;
- mLauncherEvent = launcherEvent;
- }
-
- protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
- protected abstract ActivityOptions makeLaunchOptions(Activity activity);
- protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
-
- @Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity,
- TaskIdAttributeContainer taskContainer) {
- final Task task = taskContainer.getTask();
- if (!task.isDockable) {
- return null;
- }
- if (!isAvailable(activity, task.key.displayId)) {
- return null;
- }
- return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this,
- mLauncherEvent);
- }
- }
-
class SplitSelectSystemShortcut extends SystemShortcut {
private final TaskView mTaskView;
private final SplitPositionOption mSplitPositionOption;
+
public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
SplitPositionOption option) {
super(option.iconResId, option.textResId, target, taskView.getItemInfo(), taskView);
@@ -133,19 +119,17 @@
}
}
- class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
+ class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
private Handler mHandler;
private final RecentsView mRecentsView;
private final TaskThumbnailView mThumbnailView;
private final TaskView mTaskView;
- private final MultiWindowFactory mFactory;
private final LauncherEvent mLauncherEvent;
- public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
- TaskIdAttributeContainer taskContainer, MultiWindowFactory factory,
- LauncherEvent launcherEvent) {
+ public FreeformSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer, LauncherEvent launcherEvent) {
super(iconRes, textRes, activity, taskContainer.getItemInfo(),
taskContainer.getTaskView());
mLauncherEvent = launcherEvent;
@@ -153,55 +137,30 @@
mTaskView = taskContainer.getTaskView();
mRecentsView = activity.getOverviewPanel();
mThumbnailView = taskContainer.getThumbnailView();
- mFactory = factory;
}
@Override
public void onClick(View view) {
- Task.TaskKey taskKey = mTaskView.getTask().key;
- final int taskId = taskKey.id;
-
- final View.OnLayoutChangeListener onLayoutChangeListener =
- new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int l, int t, int r, int b,
- int oldL, int oldT, int oldR, int oldB) {
- mTaskView.getRootView().removeOnLayoutChangeListener(this);
- mRecentsView.clearIgnoreResetTask(taskId);
-
- // Start animating in the side pages once launcher has been resized
- mRecentsView.dismissTask(mTaskView, false, false);
- }
- };
-
- final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
- new DeviceProfile.OnDeviceProfileChangeListener() {
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- mTarget.removeOnDeviceProfileChangeListener(this);
- if (dp.isMultiWindowMode) {
- mTaskView.getRootView().addOnLayoutChangeListener(
- onLayoutChangeListener);
- }
- }
- };
-
dismissTaskMenuView(mTarget);
+ RecentsView rv = mTarget.getOverviewPanel();
+ rv.switchToScreenshot(() -> {
+ rv.finishRecentsAnimation(true /* toHome */, () -> {
+ mTarget.returnToHomescreen();
+ rv.getHandler().post(this::startActivity);
+ });
+ });
+ }
- ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
+ private void startActivity() {
+ final Task.TaskKey taskKey = mTaskView.getTask().key;
+ final int taskId = taskKey.id;
+ final ActivityOptions options = makeLaunchOptions(mTarget);
if (options != null) {
options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
}
if (options != null
&& ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
options)) {
- if (!mFactory.onActivityStarted(mTarget)) {
- return;
- }
- // Add a device profile change listener to kick off animating the side tasks
- // once we enter multiwindow mode and relayout
- mTarget.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
-
final Runnable animStartedListener = () -> {
// Hide the task view and wait for the window to be resized
// TODO: Consider animating in launcher and do an in-place start activity
@@ -240,75 +199,105 @@
.log(mLauncherEvent);
}
}
- }
- /** @Deprecated */
- TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen,
- R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) {
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- // Don't show menu-item if already in multi-window and the task is from
- // the secondary display.
- // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
- // implementation is enabled
- return !activity.getDeviceProfile().isMultiWindowMode
- && (displayId == -1 || displayId == DEFAULT_DISPLAY);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
- activity.getDisplayId());
- if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
- return null;
- }
- boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
- return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
- }
-
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- return true;
- }
- };
-
- TaskShortcutFactory FREE_FORM = new MultiWindowFactory(R.drawable.ic_split_screen,
- R.string.recent_task_option_freeform, LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP) {
-
- @Override
- protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
- return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
- }
-
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
+ private ActivityOptions makeLaunchOptions(Activity activity) {
ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
// Arbitrary bounds only because freeform is in dev mode right now
- Rect r = new Rect(50, 50, 200, 200);
+ final View decorView = activity.getWindow().getDecorView();
+ final WindowInsets insets = decorView.getRootWindowInsets();
+ final Rect r = new Rect(0, 0, decorView.getWidth() / 2, decorView.getHeight() / 2);
+ r.offsetTo(insets.getSystemWindowInsetLeft() + 50,
+ insets.getSystemWindowInsetTop() + 50);
activityOptions.setLaunchBounds(r);
return activityOptions;
}
+ }
+ /**
+ * Does NOT add split options in the following scenarios:
+ * * The taskView to add split options is already showing split screen tasks
+ * * There aren't at least 2 tasks in overview to show split options for
+ * * Split isn't supported by the task itself (non resizable activity)
+ * * We aren't currently in multi-window
+ * * The taskView to show split options for is the focused task AND we haven't started
+ * scrolling in overview (if we haven't scrolled, there's a split overview action button so
+ * we don't need this menu option)
+ */
+ TaskShortcutFactory SPLIT_SELECT = new TaskShortcutFactory() {
@Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- activity.returnToHomescreen();
- return true;
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ DeviceProfile deviceProfile = activity.getDeviceProfile();
+ final Task task = taskContainer.getTask();
+ final TaskView taskView = taskContainer.getTaskView();
+ final RecentsView recentsView = taskView.getRecentsView();
+ final PagedOrientationHandler orientationHandler =
+ recentsView.getPagedOrientationHandler();
+
+ int[] taskViewTaskIds = taskView.getTaskIds();
+ boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
+ taskViewTaskIds[1] != -1;
+ boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2;
+ boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
+ boolean isTaskInExpectedScrollPosition =
+ recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
+ boolean isTaskSplitNotSupported = !task.isDockable;
+ boolean hideForExistingMultiWindow = activity.getDeviceProfile().isMultiWindowMode;
+
+ if (taskViewHasMultipleTasks ||
+ notEnoughTasksToSplit ||
+ isTaskSplitNotSupported ||
+ hideForExistingMultiWindow ||
+ (isFocusedTask && isTaskInExpectedScrollPosition)) {
+ return null;
+ }
+
+ return orientationHandler.getSplitPositionOptions(deviceProfile)
+ .stream()
+ .map((Function<SplitPositionOption, SystemShortcut>) option ->
+ new SplitSelectSystemShortcut(activity, taskView, option))
+ .collect(Collectors.toList());
}
};
- TaskShortcutFactory PIN = (activity, taskContainer) -> {
- if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
- return null;
+ TaskShortcutFactory FREE_FORM = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ final Task task = taskContainer.getTask();
+ if (!task.isDockable) {
+ return null;
+ }
+ if (!isAvailable(activity, task.key.displayId)) {
+ return null;
+ }
+
+ return Collections.singletonList(new FreeformSystemShortcut(R.drawable.ic_split_screen,
+ R.string.recent_task_option_freeform, activity, taskContainer,
+ LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP));
}
- if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
- return null;
+
+ private boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+ return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
}
- if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
- // We shouldn't be able to pin while an app is locked.
- return null;
+ };
+
+ TaskShortcutFactory PIN = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
+ return null;
+ }
+ if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+ return null;
+ }
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ // We shouldn't be able to pin while an app is locked.
+ return null;
+ }
+ return Collections.singletonList(new PinSystemShortcut(activity, taskContainer));
}
- return new PinSystemShortcut(activity, taskContainer);
};
class PinSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
@@ -335,26 +324,52 @@
}
}
- TaskShortcutFactory INSTALL = (activity, taskContainer) ->
- InstantAppResolver.newInstance(activity).isInstantApp(activity,
- taskContainer.getTask().getTopComponent().getPackageName())
- ? new SystemShortcut.Install(activity, taskContainer.getItemInfo(),
- taskContainer.getTaskView()) : null;
+ TaskShortcutFactory INSTALL = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ return InstantAppResolver.newInstance(activity).isInstantApp(activity,
+ taskContainer.getTask().getTopComponent().getPackageName()) ?
+ Collections.singletonList(new SystemShortcut.Install(activity,
+ taskContainer.getItemInfo(), taskContainer.getTaskView())) :
+ null;
+ }
+ };
- TaskShortcutFactory WELLBEING = (activity, taskContainer) ->
- WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo(),
- taskContainer.getTaskView());
+ TaskShortcutFactory WELLBEING = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ SystemShortcut<BaseDraggingActivity> wellbeingShortcut =
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity,
+ taskContainer.getItemInfo(), taskContainer.getTaskView());
+ return createSingletonShortcutList(wellbeingShortcut);
+ }
+ };
- TaskShortcutFactory SCREENSHOT = (activity, taskContainer) ->
- taskContainer.getThumbnailView().getTaskOverlay()
+ TaskShortcutFactory SCREENSHOT = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ SystemShortcut screenshotShortcut = taskContainer.getThumbnailView().getTaskOverlay()
.getScreenshotShortcut(activity, taskContainer.getItemInfo(),
taskContainer.getTaskView());
-
- TaskShortcutFactory MODAL = (activity, taskContainer) -> {
- if (ENABLE_OVERVIEW_SELECTIONS.get()) {
- return taskContainer.getThumbnailView().getTaskOverlay().getModalStateSystemShortcut(
- taskContainer.getItemInfo(), taskContainer.getTaskView());
+ return createSingletonShortcutList(screenshotShortcut);
}
- return null;
+ };
+
+ TaskShortcutFactory MODAL = new TaskShortcutFactory() {
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ SystemShortcut modalStateSystemShortcut =
+ taskContainer.getThumbnailView().getTaskOverlay()
+ .getModalStateSystemShortcut(
+ taskContainer.getItemInfo(), taskContainer.getTaskView());
+ if (ENABLE_OVERVIEW_SELECTIONS.get()) {
+ return createSingletonShortcutList(modalStateSystemShortcut);
+ }
+ return null;
+ }
};
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index db402af..a030568 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -468,7 +468,6 @@
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
finishCallback.run();
}
});
@@ -530,7 +529,6 @@
for (SurfaceControl leash: closingTargets) {
t.hide(leash);
}
- super.onAnimationEnd(animation);
finishCallback.run();
}
});
@@ -599,9 +597,14 @@
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
- // Make sure recents gets fixed up by resetting task alphas and scales, etc.
windowAnimEndListener = new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ recentsView.onTaskLaunchedInLiveTileMode();
+ }
+
+ // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+ @Override
public void onAnimationEnd(Animator animation) {
recentsView.finishRecentsAnimation(false /* toRecents */, () -> {
recentsView.post(() -> {
@@ -677,7 +680,6 @@
dockFadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
if (shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
t.setAlpha(leash, 0);
@@ -689,7 +691,6 @@
@Override
public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
if (!shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
t.hide(leash);
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 723dc72..42fa86d 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -33,7 +33,7 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.SplitConfigurationOptions.StageType;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitStageInfo;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -63,8 +63,9 @@
// Ordered list with first item being the most recent task.
private final LinkedList<RunningTaskInfo> mOrderedTaskList = new LinkedList<>();
- private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition();
- private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition();
+
+ private final SplitStageInfo mMainStagePosition = new SplitStageInfo();
+ private final SplitStageInfo mSideStagePosition = new SplitStageInfo();
private int mPinnedTaskId = INVALID_TASK_ID;
private TopTaskTracker(Context context) {
@@ -144,8 +145,8 @@
mPinnedTaskId = INVALID_TASK_ID;
}
- private void resetTaskId(StagedSplitTaskPosition taskPosition) {
- taskPosition.taskId = INVALID_TASK_ID;
+ private void resetTaskId(SplitStageInfo taskPosition) {
+ taskPosition.taskId = -1;
}
/**
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 61d36fb..bf1c998 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -51,6 +51,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputEvent;
@@ -127,6 +128,9 @@
private static final String TAG = "TouchInteractionService";
+ private static final boolean BUBBLES_HOME_GESTURE_ENABLED =
+ SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
+
private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
@@ -723,16 +727,30 @@
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac);
}
- // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles
- // instead of going all the way home when a swipe up is detected.
- // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
- // expanded in the back. Make sure swipe up is not passed to bubbles in this case.
- if ((mDeviceState.isBubblesExpanded() && !mDeviceState.isNotificationPanelExpanded())
- || mDeviceState.isSystemUiDialogShowing()) {
+ if (mDeviceState.isBubblesExpanded()) {
+ if (BUBBLES_HOME_GESTURE_ENABLED) {
+ // Bubbles can handle home gesture itself.
+ base = getDefaultInputConsumer();
+ } else {
+ // If Bubbles is expanded, use the overlay input consumer, which will close
+ // Bubbles instead of going all the way home when a swipe up is detected.
+ // Notification panel can be expanded on top of expanded bubbles. Bubbles remain
+ // expanded in the back. Make sure swipe up is not passed to bubbles in this
+ // case.
+ if (!mDeviceState.isNotificationPanelExpanded()) {
+ base = new SysUiOverlayInputConsumer(
+ getBaseContext(), mDeviceState, mInputMonitorCompat);
+ }
+ }
+ }
+
+ if (mDeviceState.isSystemUiDialogShowing()) {
base = new SysUiOverlayInputConsumer(
getBaseContext(), mDeviceState, mInputMonitorCompat);
}
+
+
if (mDeviceState.isScreenPinningActive()) {
// Note: we only allow accessibility to wrap this, and it replaces the previous
// base input consumer (which should be NO_OP anyway since topTaskLocked == true).
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index f68bbbc..c7c3441 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -27,6 +27,7 @@
import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.OVERVIEW_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;
@@ -105,6 +106,8 @@
boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
showAsGrid ? INSTANT : FINAL_FRAME);
+ setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, state == RecentsState.DEFAULT ? 1f : 0f,
+ INSTANT);
setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 77db6b7..af9d0cb 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
@@ -52,7 +53,7 @@
public static final RecentsState HOME = new RecentsState(3, 0);
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
- FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI);
+ FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS);
public final int ordinal;
private final int mFlags;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 3d737ca..c1750b5 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,12 +19,13 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.animation.Animator;
@@ -101,6 +102,8 @@
private boolean mThresholdCrossed = false;
private boolean mHomeLaunched = false;
+ private boolean mCancelWhenRecentsStart = false;
+ private boolean mDismissTask = false;
private RecentsAnimationController mRecentsAnimationController;
@@ -204,9 +207,20 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (dismissTask) {
- // For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(createHomeIntent());
+ if (ENABLE_SHELL_TRANSITIONS) {
+ if (mTaskAnimationManager.getCurrentCallbacks() != null) {
+ if (mRecentsAnimationController != null) {
+ finishRecentsAnimationForShell(dismissTask);
+ } else {
+ // the transition of recents animation hasn't started, wait for it
+ mCancelWhenRecentsStart = true;
+ mDismissTask = dismissTask;
+ }
+ }
+ } else if (dismissTask) {
+ // For now, just start the home intent so user is prompted to
+ // unlock the device.
+ startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
mHomeLaunched = true;
}
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
@@ -238,12 +252,24 @@
mTransformParams.setTargetSet(targets);
applyTransform();
mStateCallback.setState(STATE_TARGET_RECEIVED);
+ if (mCancelWhenRecentsStart) {
+ finishRecentsAnimationForShell(mDismissTask);
+ }
}
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
mRecentsAnimationController = null;
mTransformParams.setTargetSet(null);
+ mCancelWhenRecentsStart = false;
+ }
+
+ private void finishRecentsAnimationForShell(boolean dismissTask) {
+ mCancelWhenRecentsStart = false;
+ mTaskAnimationManager.finishRunningRecentsAnimation(dismissTask /* toHome */);
+ if (dismissTask) {
+ mHomeLaunched = true;
+ }
}
private void endRemoteAnimation() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index e458c1f..641385d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -47,6 +48,7 @@
import androidx.annotation.UiThread;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
@@ -58,7 +60,9 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
@@ -107,6 +111,7 @@
private VelocityTracker mVelocityTracker;
private AbsSwipeUpHandler mInteractionHandler;
+ private final FinishImmediatelyHandler mCleanupHandler = new FinishImmediatelyHandler();
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
@@ -377,6 +382,7 @@
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
+ mActiveCallbacks.removeListener(mCleanupHandler);
mActiveCallbacks.addListener(mInteractionHandler);
mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
@@ -414,7 +420,19 @@
}
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
- // starting the gesture. In that case, just cleanup immediately.
+ // starting the gesture. In that case, we need to clean-up an unfinished or un-started
+ // animation.
+ if (mActiveCallbacks != null && mInteractionHandler != null) {
+ if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ // The animation started, but with no movement, in this case, there will be no
+ // animateToProgress so we have to manually finish here.
+ mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+ } else {
+ // The animation hasn't started yet, so insert a replacement handler into the
+ // callbacks which immediately finishes the animation after it starts.
+ mActiveCallbacks.addListener(mCleanupHandler);
+ }
+ }
onConsumerAboutToBeSwitched();
onInteractionGestureFinished();
@@ -464,7 +482,7 @@
}
private void removeListener() {
- if (mActiveCallbacks != null) {
+ if (mActiveCallbacks != null && mInteractionHandler != null) {
mActiveCallbacks.removeListener(mInteractionHandler);
}
}
@@ -490,4 +508,19 @@
mInteractionHandler.writeToProto(inputConsumerProto);
}
}
+
+ /**
+ * A listener which just finishes the animation immediately after starting. Replaces
+ * AbsSwipeUpHandler if the gesture itself finishes before the animation even starts.
+ */
+ private static class FinishImmediatelyHandler
+ implements RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ controller.finish(false /* toRecents */, null);
+ });
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 864e08d..a730183 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,12 +15,11 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
-import android.content.ActivityNotFoundException;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
@@ -79,11 +78,7 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
- try {
- mContext.startActivity(mGestureState.getHomeIntent());
- } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
- mContext.startActivity(createHomeIntent());
- }
+ startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null);
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 47bb3cf..8ad17cb 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import android.animation.Animator;
import android.app.Activity;
@@ -154,45 +155,50 @@
}
private void startBackgroundAnimation() {
- if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_THUD)) {
- if (mBackgroundAnimatorListener == null) {
- mBackgroundAnimatorListener =
- new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect()));
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- runOnUiHelperThread(mVibrator::cancel);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- runOnUiHelperThread(mVibrator::cancel);
- }
- };
- }
- mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+ if (!Utilities.ATLEAST_S || mVibrator == null) {
+ return;
}
- mAnimatedBackground.playAnimation();
- }
+ boolean supportsThud = mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_THUD);
- /**
- * Sets up the vibration effect for the next round of animation. The parameters vary between
- * different illustrations.
- */
- private VibrationEffect getVibrationEffect() {
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50)
- .compose();
+ if (!supportsThud && !mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ return;
+ }
+ if (mBackgroundAnimatorListener == null) {
+ VibrationEffect vibrationEffect = VibrationEffect.startComposition()
+ .addPrimitive(supportsThud
+ ? VibrationEffect.Composition.PRIMITIVE_THUD
+ : VibrationEffect.Composition.PRIMITIVE_TICK,
+ /* scale= */ 1.0f,
+ /* delay= */ 50)
+ .compose();
+
+ mBackgroundAnimatorListener =
+ new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ runOnUiHelperThread(() -> mVibrator.vibrate(vibrationEffect));
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ runOnUiHelperThread(mVibrator::cancel);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ runOnUiHelperThread(mVibrator::cancel);
+ }
+ };
+ }
+ mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+ mAnimatedBackground.playAnimation();
}
@Override
@@ -302,7 +308,7 @@
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == AccessibilityAction.ACTION_CLICK.getId()) {
- startActivity(Utilities.createHomeIntent());
+ startHomeIntentSafely(AllSetActivity.this, null);
finish();
return true;
}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index e2563e3..f30d00c 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -19,7 +19,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -29,13 +29,14 @@
public class GroupTask {
public @NonNull Task task1;
public @Nullable Task task2;
- public @Nullable StagedSplitBounds mStagedSplitBounds;
+ public @Nullable
+ SplitBounds mSplitBounds;
public GroupTask(@NonNull Task t1, @Nullable Task t2,
- @Nullable StagedSplitBounds stagedSplitBounds) {
+ @Nullable SplitBounds splitBounds) {
task1 = t1;
task2 = t2;
- mStagedSplitBounds = stagedSplitBounds;
+ mSplitBounds = splitBounds;
}
public GroupTask(@NonNull GroupTask group) {
@@ -43,7 +44,7 @@
task2 = group.task2 != null
? new Task(group.task2)
: null;
- mStagedSplitBounds = group.mStagedSplitBounds;
+ mSplitBounds = group.mSplitBounds;
}
public boolean containsTask(int taskId) {
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 63d5b0d..9fe24de 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -43,7 +43,6 @@
import android.util.Log;
import android.view.View;
-import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.core.content.FileProvider;
@@ -86,67 +85,70 @@
* Launch the activity to share image for overview sharing. This is to share cropped bitmap
* with specific share targets (with shortcutInfo and appTarget) rendered in overview.
*/
- @UiThread
public static void shareImage(Context context, Supplier<Bitmap> bitmapSupplier, RectF rectF,
ShortcutInfo shortcutInfo, AppTarget appTarget, String tag) {
- if (bitmapSupplier.get() == null) {
- return;
- }
- Rect crop = new Rect();
- rectF.round(crop);
- Intent intent = new Intent();
- Uri uri = getImageUri(bitmapSupplier.get(), crop, context, tag);
- ClipData clipdata = new ClipData(new ClipDescription("content",
- new String[]{"image/png"}),
- new ClipData.Item(uri));
- intent.setAction(Intent.ACTION_SEND)
- .setComponent(new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
- .setType("image/png")
- .putExtra(Intent.EXTRA_STREAM, uri)
- .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
- .setClipData(clipdata);
+ UI_HELPER_EXECUTOR.execute(() -> {
+ Bitmap bitmap = bitmapSupplier.get();
+ if (bitmap == null) {
+ return;
+ }
+ Rect crop = new Rect();
+ rectF.round(crop);
+ Intent intent = new Intent();
+ Uri uri = getImageUri(bitmap, crop, context, tag);
+ ClipData clipdata = new ClipData(new ClipDescription("content",
+ new String[]{"image/png"}),
+ new ClipData.Item(uri));
+ intent.setAction(Intent.ACTION_SEND)
+ .setComponent(
+ new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
+ .setType("image/png")
+ .putExtra(Intent.EXTRA_STREAM, uri)
+ .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
+ .setClipData(clipdata);
- if (context.getUserId() != appTarget.getUser().getIdentifier()) {
- intent.prepareToLeaveUser(context.getUserId());
- intent.fixUris(context.getUserId());
- context.startActivityAsUser(intent, appTarget.getUser());
- } else {
- context.startActivity(intent);
- }
+ if (context.getUserId() != appTarget.getUser().getIdentifier()) {
+ intent.prepareToLeaveUser(context.getUserId());
+ intent.fixUris(context.getUserId());
+ context.startActivityAsUser(intent, appTarget.getUser());
+ } else {
+ context.startActivity(intent);
+ }
+ });
}
/**
* Launch the activity to share image.
*/
- @UiThread
public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
Rect crop, Intent intent, String tag) {
- if (bitmapSupplier.get() == null) {
- Log.e(tag, "No snapshot available, not starting share.");
- return;
- }
-
- UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
- bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
- tag));
+ UI_HELPER_EXECUTOR.execute(() -> {
+ Bitmap bitmap = bitmapSupplier.get();
+ if (bitmap == null) {
+ Log.e(tag, "No snapshot available, not starting share.");
+ return;
+ }
+ persistBitmapAndStartActivity(context, bitmap, crop, intent,
+ ImageActionUtils::getShareIntentForImageUri, tag);
+ });
}
/**
* Launch the activity to share image with shared element transition.
*/
- @UiThread
public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
Rect crop, Intent intent, String tag, View sharedElement) {
- if (bitmapSupplier.get() == null) {
- Log.e(tag, "No snapshot available, not starting share.");
- return;
- }
-
- UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
- bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
- tag, sharedElement));
+ UI_HELPER_EXECUTOR.execute(() -> {
+ Bitmap bitmap = bitmapSupplier.get();
+ if (bitmap == null) {
+ Log.e(tag, "No snapshot available, not starting share.");
+ return;
+ }
+ persistBitmapAndStartActivity(context, bitmap,
+ crop, intent, ImageActionUtils::getShareIntentForImageUri, tag, sharedElement);
+ });
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index b83e26e..e758f5b 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -17,13 +17,14 @@
import android.content.Context;
import android.content.res.Resources;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import com.android.launcher3.Alarm;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
/**
* Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
@@ -31,6 +32,8 @@
*/
public class MotionPauseDetector {
+ private static final String TAG = "MotionPauseDetector";
+
// The percentage of the previous speed that determines whether this is a rapid deceleration.
// The bigger this number, the easier it is to trigger the first pause.
private static final float RAPID_DECELERATION_FACTOR = 0.6f;
@@ -43,6 +46,12 @@
*/
private static final long HARDER_TRIGGER_TIMEOUT = 400;
+ /**
+ * When running in a test harness, if no motion is added for this amount of time, assume the
+ * motion has paused. (We use an increased timeout since sometimes test devices can be slow.)
+ */
+ private static final long TEST_HARNESS_TRIGGER_TIMEOUT = 2000;
+
private final float mSpeedVerySlow;
private final float mSpeedSlow;
private final float mSpeedSomewhatFast;
@@ -85,7 +94,8 @@
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mForcePauseTimeout = new Alarm();
- mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
+ mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */,
+ "Force pause timeout after " + alarm.getLastSetTimeout() + "ms" /* reason */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
mVelocityProvider = new SystemVelocityProvider(axis);
}
@@ -102,7 +112,7 @@
*/
public void setDisallowPause(boolean disallowPause) {
mDisallowPause = disallowPause;
- updatePaused(mIsPaused);
+ updatePaused(mIsPaused, "Set disallowPause=" + disallowPause);
}
/**
@@ -119,9 +129,11 @@
* @param pointerIndex Index for the pointer being tracked in the motion event
*/
public void addPosition(MotionEvent ev, int pointerIndex) {
- long timeoutMs = TestProtocol.sForcePauseTimeout != null
- ? TestProtocol.sForcePauseTimeout
- : mMakePauseHarderToTrigger ? HARDER_TRIGGER_TIMEOUT : FORCE_PAUSE_TIMEOUT;
+ long timeoutMs = Utilities.IS_RUNNING_IN_TEST_HARNESS
+ ? TEST_HARNESS_TRIGGER_TIMEOUT
+ : mMakePauseHarderToTrigger
+ ? HARDER_TRIGGER_TIMEOUT
+ : FORCE_PAUSE_TIMEOUT;
mForcePauseTimeout.setAlarm(timeoutMs);
float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
if (mPreviousVelocity != null) {
@@ -134,21 +146,27 @@
float speed = Math.abs(velocity);
float previousSpeed = Math.abs(prevVelocity);
boolean isPaused;
+ String isPausedReason = "";
if (mIsPaused) {
// Continue to be paused until moving at a fast speed.
isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
+ isPausedReason = "Was paused, but started moving at a fast speed";
} else {
if (velocity < 0 != prevVelocity < 0) {
// We're just changing directions, not necessarily stopping.
isPaused = false;
+ isPausedReason = "Velocity changed directions";
} else {
isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
+ isPausedReason = "Pause requires back to back slow speeds";
if (!isPaused && !mHasEverBeenPaused) {
// We want to be more aggressive about detecting the first pause to ensure it
// feels as responsive as possible; getting two very slow speeds back to back
// takes too long, so also check for a rapid deceleration.
boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
+ isPausedReason = "Didn't have back to back slow speeds, checking for rapid"
+ + " deceleration on first pause only";
}
if (mMakePauseHarderToTrigger) {
if (speed < mSpeedSlow) {
@@ -156,22 +174,27 @@
mSlowStartTime = time;
}
isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT;
+ isPausedReason = "Maintained slow speed for sufficient duration when making"
+ + " pause harder to trigger";
} else {
mSlowStartTime = 0;
isPaused = false;
+ isPausedReason = "Intentionally making pause harder to trigger";
}
}
}
}
- updatePaused(isPaused);
+ updatePaused(isPaused, isPausedReason);
}
- private void updatePaused(boolean isPaused) {
+ private void updatePaused(boolean isPaused, String reason) {
if (mDisallowPause) {
+ reason = "Disallow pause; otherwise, would have been " + isPaused + " due to " + reason;
isPaused = false;
}
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
+ Log.d(TAG, "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason);
boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
if (mIsPaused) {
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 6038a22..dec934a 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -53,7 +53,6 @@
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.views.TaskView;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -221,8 +220,7 @@
private boolean updateHandler() {
mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
- if (mRecentsActivityRotation == mTouchRotation || (isRecentsActivityRotationAllowed()
- && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
+ if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
} else if (mTouchRotation == ROTATION_90) {
mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
@@ -400,37 +398,10 @@
* Returns the scale and pivot so that the provided taskRect can fit the provided full size
*/
public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) {
- Rect insets = dp.getInsets();
- float fullWidth = dp.widthPx;
- float fullHeight = dp.heightPx;
- if (TaskView.clipLeft(dp)) {
- fullWidth -= insets.left;
- }
- if (TaskView.clipRight(dp)) {
- fullWidth -= insets.right;
- }
- if (TaskView.clipTop(dp)) {
- fullHeight -= insets.top;
- }
- if (TaskView.clipBottom(dp)) {
- fullHeight -= insets.bottom;
- }
-
- getTaskDimension(mContext, dp, outPivot);
+ getTaskDimension(dp, outPivot);
float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height());
- // We also scale the preview as part of fullScreenParams, so account for that as well.
- if (fullWidth > 0) {
- scale = scale * dp.widthPx / fullWidth;
- }
-
if (scale == 1) {
- outPivot.set(fullWidth / 2, fullHeight / 2);
- } else if (dp.isMultiWindowMode) {
- float denominator = 1 / (scale - 1);
- // Ensure that the task aligns to right bottom for the root view
- float y = (scale * taskView.bottom - fullHeight) * denominator;
- float x = (scale * taskView.right - fullWidth) * denominator;
- outPivot.set(x, y);
+ outPivot.set(taskView.centerX(), taskView.centerY());
} else {
float factor = scale / (scale - 1);
outPivot.set(taskView.left * factor, taskView.top * factor);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 2502359..e89c842 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -42,6 +42,8 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.SystemUiProxy;
@@ -171,6 +173,8 @@
public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
@Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
// Assume initial task is for top/left part of screen
final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
? new int[]{taskId1, taskId2}
@@ -330,4 +334,8 @@
private boolean isInitialTaskIntentSet() {
return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null);
}
+
+ public int getInitialTaskId() {
+ return mInitialTaskId;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 32e08ff..de527a7 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -41,6 +41,7 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
@@ -60,10 +61,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;
+ public static final int DURATION_TASKBAR_MS =
+ QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
private static final float MAX_VELOCITY_PX_PER_S = 22f;
@@ -91,16 +92,20 @@
mSpringTransY = transFactor * launcher.getResources()
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
+ DeviceProfile grid = launcher.getDeviceProfile();
+ long duration = grid.isTaskbarPresent ? DURATION_TASKBAR_MS : DURATION_MS;
if (staggerWorkspace) {
- DeviceProfile grid = launcher.getDeviceProfile();
Workspace<?> workspace = launcher.getWorkspace();
Hotseat hotseat = launcher.getHotseat();
- // Hotseat and QSB takes up two additional rows.
- int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
+ boolean staggerHotseat = !grid.isVerticalBarLayout() && !grid.isTaskbarPresent;
+ boolean staggerQsb =
+ !grid.isVerticalBarLayout() && !(grid.isTaskbarPresent && grid.isQsbInline);
+ int totalRows = grid.inv.numRows + (staggerHotseat ? 1 : 0) + (staggerQsb ? 1 : 0);
// Add animation for all the visible workspace pages
- workspace.forEachVisiblePage(page -> addAnimationForPage((CellLayout) page, totalRows));
+ workspace.forEachVisiblePage(
+ page -> addAnimationForPage((CellLayout) page, totalRows, duration));
boolean workspaceClipChildren = workspace.getClipChildren();
boolean workspaceClipToPadding = workspace.getClipToPadding();
@@ -119,23 +124,34 @@
View child = hotseatIcons.getChildAt(i);
CellLayout.LayoutParams lp =
((CellLayout.LayoutParams) child.getLayoutParams());
- addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
+ addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
}
} else {
final int hotseatRow, qsbRow;
if (grid.isTaskbarPresent) {
- qsbRow = grid.inv.numRows + 1;
- hotseatRow = grid.inv.numRows + 2;
+ if (grid.isQsbInline) {
+ qsbRow = grid.inv.numRows + 1;
+ hotseatRow = grid.inv.numRows + 1;
+ } else {
+ qsbRow = grid.inv.numRows + 1;
+ hotseatRow = grid.inv.numRows + 2;
+ }
} else {
hotseatRow = grid.inv.numRows + 1;
qsbRow = grid.inv.numRows + 2;
}
- for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
- View child = hotseatIcons.getChildAt(i);
- addStaggeredAnimationForView(child, hotseatRow, totalRows);
- }
- addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
+ // Do not stagger hotseat as a whole when taskbar is present, and stagger QSB only
+ // if it's not inline.
+ if (staggerHotseat) {
+ for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+ View child = hotseatIcons.getChildAt(i);
+ addStaggeredAnimationForView(child, hotseatRow, totalRows, duration);
+ }
+ }
+ if (staggerQsb) {
+ addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows, duration);
+ }
}
mAnimators.addListener(new AnimatorListenerAdapter() {
@@ -153,19 +169,19 @@
mAnimators.addListener(forEndCallback(launcher::resumeExpensiveViewUpdates));
if (animateOverviewScrim) {
- PendingAnimation pendingAnimation = new PendingAnimation(DURATION_MS);
+ PendingAnimation pendingAnimation = new PendingAnimation(duration);
launcher.getWorkspace().getStateTransitionAnimation()
.setScrim(pendingAnimation, NORMAL, new StateAnimationConfig());
mAnimators.play(pendingAnimation.buildAnim());
}
- addDepthAnimationForState(launcher, NORMAL, DURATION_MS);
+ addDepthAnimationForState(launcher, NORMAL, duration);
mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)
- .setDuration(DURATION_MS));
+ .setDuration(duration));
}
- private void addAnimationForPage(CellLayout page, int totalRows) {
+ private void addAnimationForPage(CellLayout page, int totalRows, long duration) {
ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets();
boolean pageClipChildren = page.getClipChildren();
@@ -178,7 +194,7 @@
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);
+ addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
}
mAnimators.addListener(new AnimatorListenerAdapter() {
@@ -231,8 +247,9 @@
* @param v A view on the workspace.
* @param row The bottom-most row that contains the view.
* @param totalRows Total number of rows.
+ * @param duration duration of the animation
*/
- private void addStaggeredAnimationForView(View v, int row, int totalRows) {
+ private void addStaggeredAnimationForView(View v, int row, int totalRows, long duration) {
if (mIgnoredView != null && mIgnoredView == v) return;
// Invert the rows, because we stagger starting from the bottom of the screen.
int invertedRow = totalRows - row;
@@ -266,7 +283,7 @@
v.setAlpha(0);
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
alpha.setInterpolator(LINEAR);
- alpha.setDuration(ALPHA_DURATION_MS);
+ alpha.setDuration(duration);
alpha.setStartDelay(startDelay);
alpha.addListener(new AnimatorListenerAdapter() {
@Override
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index b222f51..74e4acc 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -253,7 +253,7 @@
rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
} else {
return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mAppBounds,
- bounds, insets);
+ bounds, insets, progress);
}
}
@@ -279,7 +279,10 @@
// get the final leash operations but do not apply to the leash.
final SurfaceControl.Transaction tx =
PipSurfaceTransactionHelper.newSurfaceControlTransaction();
- return onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS);
+ final PictureInPictureSurfaceTransaction pipTx =
+ onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS);
+ pipTx.setShouldDisableCanAffectSystemUiFlags(true);
+ return pipTx;
}
private RotatedPosition getRotatedPosition(float progress) {
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index 9bb3d56..5dc4613 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -15,12 +15,20 @@
*/
package com.android.quickstep.util;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.view.Display;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.view.Surface;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import java.util.Set;
+
/**
* Extension of {@link WindowManagerProxy} with some assumption for the default system Launcher
*/
@@ -31,23 +39,23 @@
}
@Override
- protected String getDisplayId(Display display) {
- return display.getUniqueId();
+ public int getRotation(Context displayInfoContext) {
+ return displayInfoContext.getResources().getConfiguration().windowConfiguration
+ .getRotation();
}
@Override
- public boolean isInternalDisplay(Display display) {
- return display.getType() == Display.TYPE_INTERNAL;
- }
-
- @Override
- public int getRotation(Context context) {
- return context.getResources().getConfiguration().windowConfiguration.getRotation();
- }
-
- @Override
- protected Display[] getDisplays(Context context) {
- return context.getSystemService(DisplayManager.class).getDisplays(
- DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ Context displayInfoContext) {
+ ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ WindowManager windowManager = displayInfoContext.getSystemService(WindowManager.class);
+ Set<WindowMetrics> possibleMaximumWindowMetrics =
+ windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
+ for (WindowMetrics windowMetrics : possibleMaximumWindowMetrics) {
+ CachedDisplayInfo info = getDisplayInfo(windowMetrics, Surface.ROTATION_0);
+ WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
+ result.put(info, bounds);
+ }
+ return result;
}
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 5212755..d37300c 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@@ -40,7 +41,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
@@ -100,7 +101,7 @@
// Cached calculations
private boolean mLayoutValid = false;
private int mOrientationStateId;
- private StagedSplitBounds mStagedSplitBounds;
+ private SplitBounds mSplitBounds;
private boolean mDrawsBelowRecents;
private boolean mIsGridTask;
private int mTaskRectTranslationX;
@@ -152,13 +153,13 @@
}
Rect fullTaskSize;
- if (mStagedSplitBounds != null) {
+ if (mSplitBounds != null) {
// The task rect changes according to the staged split task sizes, but recents
// fullscreen scale and pivot remains the same since the task fits into the existing
// sized task space bounds
fullTaskSize = new Rect(mTaskRect);
mOrientationState.getOrientationHandler()
- .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
+ .setSplitTaskSwipeRect(mDp, mTaskRect, mSplitBounds, mStagePosition);
mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
} else {
fullTaskSize = mTaskRect;
@@ -180,10 +181,10 @@
*
* @param splitInfo set to {@code null} when not in staged split mode
*/
- public void setPreview(RemoteAnimationTargetCompat runningTarget, StagedSplitBounds splitInfo) {
+ public void setPreview(RemoteAnimationTargetCompat runningTarget, SplitBounds splitInfo) {
setPreview(runningTarget);
- mStagedSplitBounds = splitInfo;
- if (mStagedSplitBounds == null) {
+ mSplitBounds = splitInfo;
+ if (mSplitBounds == null) {
mStagePosition = STAGE_POSITION_UNDEFINED;
return;
}
@@ -390,10 +391,15 @@
.withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius());
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
- // When relativeLayer = 0, it reverts the surfaces back to the original order.
- builder.withRelativeLayerTo(params.getRecentsSurface(),
- mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // In legacy transitions, the animation leashes remain in same hierarchy in the
+ // TaskDisplayArea, so we don't want to bump the layer too high otherwise it will
+ // conflict with layers that WM core positions (ie. the input consumers). For shell
+ // transitions, the animation leashes are reparented to an animation container so we
+ // can bump layers as needed.
+ builder.withLayer(mDrawsBelowRecents
+ ? Integer.MIN_VALUE + 1
+ : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
new file mode 100644
index 0000000..66bff73
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import android.os.UserHandle;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Listener for receiving various task properties changes
+ */
+public interface TaskVisualsChangeListener {
+
+ /**
+ * Called when the task thumbnail changes
+ */
+ default Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+ return null;
+ }
+
+ /**
+ * Called when the icon for a task changes
+ */
+ default void onTaskIconChanged(String pkg, UserHandle user) {}
+
+ /**
+ * Called when the icon for a task changes
+ */
+ default void onTaskIconChanged(int taskId) {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 75d6001..a7f25d4 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -139,10 +139,12 @@
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+ final int appLength = targets.unfilteredApps.length;
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
+ SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ for (int i = 0; i < appLength; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
@@ -166,6 +168,12 @@
}
surfaceParams[i] = builder.build();
}
+ // always put wallpaper layer to bottom.
+ for (int i = 0; i < wallpaperLength; i++) {
+ RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
+ surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
+ .withLayer(Integer.MIN_VALUE).build();
+ }
return surfaceParams;
}
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 79b15c7..76552a3 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -53,7 +53,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.systemui.shared.recents.model.Task;
import java.lang.annotation.Retention;
@@ -103,7 +103,7 @@
*/
private float mModalOffset = 0f;
@Nullable
- private StagedSplitBounds mStagedSplitBounds;
+ private SplitBounds mSplitBounds;
private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
private float mSplitOffsetTranslationY;
private float mSplitOffsetTranslationX;
@@ -164,9 +164,9 @@
});
}
- public void setSplitConfiguration(StagedSplitBounds stagedSplitBounds) {
- mStagedSplitBounds = stagedSplitBounds;
- if (mStagedSplitBounds == null
+ public void setSplitConfiguration(SplitBounds splitBounds) {
+ mSplitBounds = splitBounds;
+ if (mSplitBounds == null
|| !mActivity.getDeviceProfile().isTablet
|| mTaskView.isFocusedTask()) {
mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
@@ -180,11 +180,11 @@
}
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
- if (mTask.key.id == mStagedSplitBounds.leftTopTaskId) {
- mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
+ if (mTask.key.id == mSplitBounds.leftTopTaskId) {
+ mSplitBannerConfig = mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
} else {
- mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
+ mSplitBannerConfig = mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
}
}
@@ -321,7 +321,7 @@
PagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
Pair<Float, Float> translations = orientationHandler
.getDwbLayoutTranslations(mTaskView.getMeasuredWidth(),
- mTaskView.getMeasuredHeight(), mStagedSplitBounds, deviceProfile,
+ mTaskView.getMeasuredHeight(), mSplitBounds, deviceProfile,
mTaskView.getThumbnails(), mTask.key.id, mBanner);
mSplitOffsetTranslationX = translations.first;
mSplitOffsetTranslationY = translations.second;
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index c980d1e..835c9f7 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -19,6 +20,7 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.LauncherAnimUtils;
@@ -110,13 +112,19 @@
*/
public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
View originalView, @Nullable Bitmap thumbnail, Drawable icon, RectF positionOut) {
- final BaseDragLayer dragLayer = launcher.getDragLayer();
- ViewGroup parent = (ViewGroup) dragLayer.getParent();
+ final ViewGroup dragLayer = launcher.getDragLayer();
final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
- .inflate(R.layout.floating_split_select_view, parent, false);
+ .inflate(R.layout.floating_split_select_view, dragLayer, false);
floatingView.init(launcher, originalView, thumbnail, icon, positionOut);
- parent.addView(floatingView);
+ // Add this animating view underneath the existing open task menu view (if there is one)
+ View openTaskView = AbstractFloatingView.getOpenView(launcher, TYPE_TASK_MENU);
+ int openTaskViewIndex = dragLayer.indexOfChild(openTaskView);
+ if (openTaskViewIndex == -1) {
+ // Add to top if not
+ openTaskViewIndex = dragLayer.getChildCount();
+ }
+ dragLayer.addView(floatingView, openTaskViewIndex - 1);
return floatingView;
}
@@ -125,7 +133,7 @@
Utilities.getBoundsForViewInDragLayer(mActivity.getDragLayer(), originalView, viewBounds,
false /* ignoreTransform */, null /* recycle */,
mStartingPosition);
- final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
+ final BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(
Math.round(mStartingPosition.width()),
Math.round(mStartingPosition.height()));
initPosition(mStartingPosition, lp);
@@ -246,7 +254,7 @@
* offscreen).
*/
void centerIconView(IconView iconView, float onScreenRectCenterX, float onScreenRectCenterY) {
- mOrientationHandler.updateStagedSplitIconParams(iconView, onScreenRectCenterX,
+ mOrientationHandler.updateSplitIconParams(iconView, onScreenRectCenterX,
onScreenRectCenterY, mFullscreenParams.mScaleX, mFullscreenParams.mScaleY,
iconView.getDrawableWidth(), iconView.getDrawableHeight(),
mActivity.getDeviceProfile(), mStagePosition);
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 244a794..2dff18e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -17,7 +18,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -53,7 +54,7 @@
private CancellableTask mIconLoadRequest2;
private final float[] mIcon2CenterCoords = new float[2];
private TransformingTouchDelegate mIcon2TouchDelegate;
- @Nullable private StagedSplitBounds mSplitBoundsConfig;
+ @Nullable private SplitBounds mSplitBoundsConfig;
private final DigitalWellBeingToast mDigitalWellBeingToast2;
public GroupedTaskView(Context context) {
@@ -78,7 +79,7 @@
}
public void bind(Task primary, Task secondary, RecentsOrientedState orientedState,
- @Nullable StagedSplitBounds splitBoundsConfig) {
+ @Nullable SplitBounds splitBoundsConfig) {
super.bind(primary, orientedState);
mSecondaryTask = secondary;
mTaskIdContainer[1] = secondary.key.id;
@@ -126,8 +127,8 @@
}
}
- public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
- mSplitBoundsConfig = stagedSplitBounds;
+ public void updateSplitBoundsConfig(SplitBounds splitBounds) {
+ mSplitBoundsConfig = splitBounds;
invalidate();
}
@@ -313,4 +314,11 @@
mSnapshotView2.setDimAlpha(amount);
mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
}
+
+ @Override
+ protected void applyThumbnailSplashAlpha() {
+ super.applyThumbnailSplashAlpha();
+ mSnapshotView2.setSplashAlpha(
+ Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 306ebd7..a736583 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -39,6 +39,7 @@
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.util.SplitSelectStateController;
@@ -89,6 +90,21 @@
}
@Override
+ public void onTaskIconChanged(int taskId) {
+ // If Launcher needs to return to split select state, do it now, after the icon has updated.
+ if (mActivity.hasPendingSplitSelectInfo()) {
+ PendingSplitSelectInfo recoveryData = mActivity.getPendingSplitSelectInfo();
+ if (recoveryData.getStagedTaskId() == taskId) {
+ initiateSplitSelect(
+ getTaskViewByTaskId(recoveryData.getStagedTaskId()),
+ recoveryData.getStagePosition()
+ );
+ mActivity.finishSplitSelectRecovery();
+ }
+ }
+ }
+
+ @Override
public void reset() {
super.reset();
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1c4e497..62ec0ef 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -22,10 +22,8 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -33,7 +31,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.MultiValueAlpha;
@@ -80,8 +77,9 @@
private static final int INDEX_VISIBILITY_ALPHA = 1;
private static final int INDEX_FULLSCREEN_ALPHA = 2;
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
+ private static final int INDEX_SHARE_TARGET_ALPHA = 4;
- private final MultiValueAlpha mMultiValueAlpha;
+ private MultiValueAlpha mMultiValueAlpha;
private Button mSplitButton;
@ActionsHiddenFlags
@@ -107,13 +105,14 @@
public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, 0);
- mMultiValueAlpha = new MultiValueAlpha(this, 5);
- mMultiValueAlpha.setUpdateVisibility(true);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5);
+ mMultiValueAlpha.setUpdateVisibility(true);
+
findViewById(R.id.action_screenshot).setOnClickListener(this);
mSplitButton = findViewById(R.id.action_split);
@@ -195,6 +194,10 @@
return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
}
+ public AlphaProperty getShareTargetAlpha() {
+ return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA);
+ }
+
/**
* Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
*/
@@ -202,10 +205,13 @@
if (mDp == null) {
return;
}
- boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent && !mDp.isGestureMode;
- if (alignFor3ButtonTaskbar) {
+ boolean largeScreenLandscape = mDp.isTablet && !mDp.isTwoPanels && mDp.isLandscape;
+ // If in 3-button mode, shift action buttons to accommodate 3-button layout.
+ // (Special exception for landscape tablets, where there is enough room and we don't need to
+ // shift the action buttons.)
+ if (mDp.areNavButtonsInline && !largeScreenLandscape) {
// Add extra horizontal spacing
- int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
+ int additionalPadding = mDp.hotseatBarEndOffset;
if (isLayoutRtl()) {
setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0);
} else {
@@ -233,10 +239,6 @@
return 0;
}
- if (mDp.isVerticalBarLayout()) {
- return mDp.getInsets().bottom;
- }
-
if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
return mDp.getOverviewActionsClaimedSpaceBelow();
}
@@ -254,12 +256,6 @@
mTaskSize.set(taskSize);
updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing,
- ViewGroup.LayoutParams.MATCH_PARENT);
- params.weight = dp.isVerticalBarLayout() ? 1 : 0;
- findViewById(R.id.action_split_space).setLayoutParams(params);
-
requestLayout();
mSplitButton.setCompoundDrawablesWithIntrinsicBounds(
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2360396..cf93857 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -34,6 +34,7 @@
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_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -140,8 +141,8 @@
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
import com.android.launcher3.util.ViewPool;
@@ -151,7 +152,6 @@
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
@@ -169,6 +169,7 @@
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.plugins.ResourceProvider;
@@ -362,6 +363,10 @@
}
};
+ /**
+ * Progress of Recents view from carousel layout to grid layout. If Recents is not shown as a
+ * grid, then the value remains 0.
+ */
public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
new FloatProperty<RecentsView>("recentsGrid") {
@Override
@@ -375,6 +380,23 @@
}
};
+ /**
+ * Progress to and from the OVERVIEW state, where being in OverviewState has a value of 1, and
+ * being in any other LauncherState has a value of 0.
+ */
+ public static final FloatProperty<RecentsView> OVERVIEW_PROGRESS =
+ new FloatProperty<RecentsView>("overviewProgress") {
+ @Override
+ public void setValue(RecentsView view, float overviewProgress) {
+ view.setOverviewProgress(overviewProgress);
+ }
+
+ @Override
+ public Float get(RecentsView view) {
+ return view.mOverviewProgress;
+ }
+ };
+
// OverScroll constants
private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -465,6 +487,7 @@
protected float mTaskViewsSecondarySplitTranslation = 0;
// Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
private float mGridProgress = 0;
+ private float mOverviewProgress = 0;
private boolean mShowAsGridLastOnLayout = false;
private final IntSet mTopRowIdSet = new IntSet();
@@ -624,12 +647,14 @@
@Nullable
private View mSecondSplitHiddenView;
@Nullable
- private StagedSplitBounds mSplitBoundsConfig;
+ private SplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
+ private SplitInstructionsView mSplitInstructionsView;
+
@Nullable
private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
@@ -1445,11 +1470,11 @@
if (hasMultipleTasks) {
boolean firstTaskIsLeftTopTask =
- groupTask.mStagedSplitBounds.leftTopTaskId == groupTask.task1.key.id;
+ groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
- groupTask.mStagedSplitBounds);
+ groupTask.mSplitBounds);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
@@ -2209,6 +2234,11 @@
}
}
}
+ if (animatorSet == null) {
+ setOverviewProgress(1);
+ } else {
+ animatorSet.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1));
+ }
}
/**
@@ -2300,7 +2330,7 @@
boolean runningTaskTileHidden = mRunningTaskTileHidden;
setCurrentTask(runningTaskViewId);
mFocusedTaskViewId = runningTaskViewId;
- setCurrentPage(getRunningTaskIndex());
+ runOnPageScrollsInitialized(() -> setCurrentPage(getRunningTaskIndex()));
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
// Update task size after setting current task.
@@ -2658,6 +2688,18 @@
mClearAllButton.setGridProgress(gridProgress);
}
+ private void setOverviewProgress(float overviewProgress) {
+ int taskCount = getTaskViewCount();
+ if (taskCount == 0) {
+ return;
+ }
+
+ mOverviewProgress = overviewProgress;
+ for (int i = 0; i < taskCount; i++) {
+ requireTaskViewAt(i).setOverviewProgress(overviewProgress);
+ }
+ }
+
private void enableLayoutTransitions() {
if (mLayoutTransition == null) {
mLayoutTransition = new LayoutTransition();
@@ -2764,11 +2806,15 @@
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
+
+ mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
+ mSplitInstructionsView.setAlpha(0);
+ anim.addFloat(mSplitInstructionsView, SplitInstructionsView.ALPHA_FLOAT, 0, 1, ACCEL);
+
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
anim.addEndListener(success -> {
if (success) {
- mSplitToast.show();
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
@@ -4007,7 +4053,8 @@
stagePosition);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- finishRecentsAnimation(true, null);
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ null /* onFinishComplete */);
}
}
@@ -4091,6 +4138,10 @@
mSecondSplitHiddenView.setVisibility(INVISIBLE);
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
+
+ // Fade out all other views underneath placeholders
+ ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0);
+ pendingAnimation.add(tvFade, DEACCEL_2, SpringProperty.DEFAULT);
pendingAnimation.buildAnim().start();
return true;
}
@@ -4099,12 +4150,16 @@
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
+ if (mSplitInstructionsView != null) {
+ mActivity.getDragLayer().removeView(mSplitInstructionsView);
+ mSplitInstructionsView = null;
+ }
if (mFirstFloatingTaskView != null) {
- mActivity.getRootView().removeView(mFirstFloatingTaskView);
+ mActivity.getDragLayer().removeView(mFirstFloatingTaskView);
mFirstFloatingTaskView = null;
}
if (mSecondFloatingTaskView != null) {
- mActivity.getRootView().removeView(mSecondFloatingTaskView);
+ mActivity.getDragLayer().removeView(mSecondFloatingTaskView);
mSecondFloatingTaskView = null;
mSecondSplitHiddenView.setVisibility(VISIBLE);
mSecondSplitHiddenView = null;
@@ -4164,6 +4219,10 @@
taskViewsFloat.first.set(this, getSplitSelectTranslation());
taskViewsFloat.second.set(this, 0f);
+ if (mSplitInstructionsView != null) {
+ mSplitInstructionsView.ensureProperRotation();
+ }
+
applySplitPrimaryScrollOffset();
}
@@ -4281,6 +4340,7 @@
properties));
}
}
+ anim.play(ObjectAnimator.ofFloat(recentsView, OVERVIEW_PROGRESS, 1, 0));
return anim;
}
@@ -4340,6 +4400,8 @@
BACKGROUND_APP.getDepth(mActivity));
anim.play(depthAnimator);
}
+ anim.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1f, 0f));
+
anim.play(progressAnim);
anim.setInterpolator(interpolator);
@@ -4473,7 +4535,7 @@
RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(
getContext(), recentsAnimationTargets);
- mSplitBoundsConfig = gluer.getStagedSplitBounds();
+ mSplitBoundsConfig = gluer.getSplitBounds();
// Add release check to the targets from the RemoteTargetGluer and not the targets
// passed in because in the event we're in split screen, we use the passed in targets
// to create new RemoteAnimationTargets in assignTargetsForSplitScreen(), and the
@@ -4523,12 +4585,6 @@
@Nullable Runnable onFinishComplete) {
// TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
cleanupRemoteTargets();
- if (!toRecents && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- // Reset the minimized state since we force-toggled the minimized state when entering
- // overview, but never actually finished the recents animation. This is a catch all for
- // cases where we haven't already reset it.
- SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false);
- }
if (mRecentsAnimationController == null) {
if (onFinishComplete != null) {
@@ -4549,6 +4605,7 @@
new PictureInPictureSurfaceTransaction.Builder()
.setAlpha(0f)
.build();
+ tx.setShouldDisableCanAffectSystemUiFlags(false);
int[] taskIds = TopTaskTracker.INSTANCE.get(getContext()).getRunningSplitTaskIds();
for (int taskId : taskIds) {
mRecentsAnimationController.setFinishTaskTransaction(taskId,
@@ -4859,10 +4916,10 @@
}
private void updateEnabledOverlays() {
- int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
+ TaskView taskView = requireTaskViewAt(i);
+ taskView.setOverlayEnabled(mOverlayEnabled && isTaskViewFullyVisible(taskView));
}
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
new file mode 100644
index 0000000..7d94505
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.DisplayController;
+
+/**
+ * A rounded rectangular component containing a single TextView.
+ * Appears when a split is in progress, and tells the user to select a second app to initiate
+ * splitscreen.
+ *
+ * Appears and disappears concurrently with a FloatingTaskView.
+ */
+public class SplitInstructionsView extends FrameLayout {
+ private final StatefulActivity mLauncher;
+
+ public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
+ @Override
+ public void setValue(SplitInstructionsView splitInstructionsView, float v) {
+ splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
+ splitInstructionsView.setAlpha(v);
+ }
+
+ @Override
+ public Float get(SplitInstructionsView splitInstructionsView) {
+ return splitInstructionsView.getAlpha();
+ }
+ };
+
+ public SplitInstructionsView(Context context) {
+ this(context, null);
+ }
+
+ public SplitInstructionsView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SplitInstructionsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = (StatefulActivity) context;
+ }
+
+ static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
+ ViewGroup dragLayer = launcher.getDragLayer();
+ final SplitInstructionsView splitInstructionsView =
+ (SplitInstructionsView) launcher.getLayoutInflater().inflate(
+ R.layout.split_instructions_view,
+ dragLayer,
+ false
+ );
+
+ dragLayer.addView(splitInstructionsView);
+ return splitInstructionsView;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ ensureProperRotation();
+ }
+
+ void ensureProperRotation() {
+ ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
+ .setSplitInstructionsParams(
+ this,
+ mLauncher.getDeviceProfile(),
+ getMeasuredHeight(),
+ getMeasuredWidth(),
+ getThreeButtonNavShift()
+ );
+ }
+
+ // In some cases, when user is using 3-button nav, there isn't enough room for both the
+ // 3-button nav and a centered SplitInstructionsView. This function will return an int that will
+ // be used to shift the SplitInstructionsView over a bit so that everything looks well-spaced.
+ // In many cases, this will return 0, since we don't need to shift it away from the center.
+ int getThreeButtonNavShift() {
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
+ && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))) {
+ int navButtonWidth = getResources().getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_size);
+ int extraMargin = getResources().getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_margin);
+ // Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
+ // up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
+ // center of the screen and we want to center it in the remaining space, therefore we
+ // want to shift it over by half the 3-button layout's width.
+ // If the user is using an RtL layout, we shift it the opposite way.
+ return -((3 * navButtonWidth + extraMargin) / 2) * (isLayoutRtl() ? -1 : 1);
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 3803f1b..c1711d1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
@@ -232,8 +231,7 @@
private void addMenuOptions(TaskIdAttributeContainer taskContainer) {
mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
mTaskName.setOnClickListener(v -> close(true));
- TaskOverlayFactory.getEnabledShortcuts(mTaskView, mActivity.getDeviceProfile(),
- taskContainer)
+ TaskOverlayFactory.getEnabledShortcuts(mTaskView, taskContainer)
.forEach(this::addMenuOption);
}
@@ -245,17 +243,9 @@
LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
menuOptionView, mActivity.getDeviceProfile());
- menuOptionView.setOnClickListener(view -> {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = mTaskView.getRecentsView();
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */,
- false /* shouldPip */,
- () -> menuOption.onClick(view)));
- } else {
- menuOption.onClick(view);
- }
- });
+ // Set an onClick listener on each menu option. The onClick method is responsible for
+ // ending LiveTile mode on the thumbnail if needed.
+ menuOptionView.setOnClickListener(menuOption::onClick);
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 06a5793..b586ac3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -164,7 +164,7 @@
private fun addMenuOptions() {
// Add the options
TaskOverlayFactory
- .getEnabledShortcuts(taskView, mActivityContext.deviceProfile, taskContainer)
+ .getEnabledShortcuts(taskView, taskContainer)
.forEach { this.addMenuOption(it) }
// Add the spaces between items
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index d8120ff..f4b3d98 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -36,12 +36,14 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.Surface;
import android.view.View;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -50,8 +52,10 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.Task;
@@ -63,6 +67,7 @@
public class TaskThumbnailView extends View {
private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
new MainThreadInitializedObject<>(FullscreenDrawParams::new);
+ private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -82,6 +87,7 @@
private TaskOverlay mOverlay;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mClearPaint = new Paint();
private final Paint mDimmingPaintAfterClearing = new Paint();
private final int mDimColor;
@@ -90,6 +96,8 @@
private final Rect mPreviewRect = new Rect();
private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper();
private TaskView.FullscreenDrawParams mFullscreenParams;
+ private ImageView mSplashView;
+ private Drawable mSplashViewDrawable;
@Nullable
private Task mTask;
@@ -100,6 +108,8 @@
/** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */
private float mDimAlpha = 0f;
+ /** Controls visibility of the splash view, 0 is transparent, 255 fully opaque. */
+ private int mSplashAlpha = 0;
private boolean mOverlayEnabled;
@@ -115,6 +125,7 @@
super(context, attrs, defStyleAttr);
mPaint.setFilterBitmap(true);
mBackgroundPaint.setColor(Color.WHITE);
+ mSplashBackgroundPaint.setColor(Color.WHITE);
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mActivity = BaseActivity.fromContext(context);
// Initialize with placeholder value. It is overridden later by TaskView
@@ -134,6 +145,8 @@
int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
mPaint.setColor(color);
mBackgroundPaint.setColor(color);
+ mSplashBackgroundPaint.setColor(color);
+ updateSplashView(mTask.icon);
}
/**
@@ -151,6 +164,9 @@
boolean thumbnailWasNull = mThumbnailData == null;
mThumbnailData =
(thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
+ if (mTask != null) {
+ updateSplashView(mTask.icon);
+ }
if (refreshNow) {
refresh(thumbnailWasNull && mThumbnailData != null);
}
@@ -201,6 +217,18 @@
updateThumbnailPaintFilter();
}
+ /**
+ * Sets the alpha of the splash view.
+ */
+ public void setSplashAlpha(float splashAlpha) {
+ mSplashAlpha = (int) (Utilities.boundToRange(splashAlpha, 0f, 1f) * 255);
+ if (mSplashViewDrawable != null) {
+ mSplashViewDrawable.setAlpha(mSplashAlpha);
+ }
+ mSplashBackgroundPaint.setAlpha(mSplashAlpha);
+ invalidate();
+ }
+
public TaskOverlay getTaskOverlay() {
if (mOverlay == null) {
mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
@@ -237,16 +265,13 @@
boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
DeviceProfile dp = mActivity.getDeviceProfile();
- int leftInset = TaskView.clipLeft(dp) ? Math.round(boundsInBitmapSpace.left) : 0;
- int topInset = TaskView.clipTop(dp) ? Math.round(boundsInBitmapSpace.top) : 0;
- int rightInset = TaskView.clipRight(dp) ? Math.round(
- bitmapRect.right - boundsInBitmapSpace.right) : 0;
- int bottomInset = TaskView.clipBottom(dp)
+ int bottomInset = dp.isTablet
? Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom) : 0;
- return Insets.of(leftInset, topInset, rightInset, bottomInset);
+ return Insets.of(0, 0, 0, bottomInset);
}
+ @SystemUiControllerFlags
public int getSysUiStatusNavFlags() {
if (mThumbnailData != null) {
int flags = 0;
@@ -262,6 +287,12 @@
}
@Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateSplashView(mSplashViewDrawable);
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets;
canvas.save();
@@ -311,6 +342,17 @@
}
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+
+ // Draw splash above thumbnail to hide inconsistencies in rotation and aspect ratios.
+ if (shouldShowSplashView()) {
+ if (mSplashView != null) {
+ canvas.drawRoundRect(x, y, width + 1, height + 1, cornerRadius,
+ cornerRadius, mSplashBackgroundPaint);
+
+ mSplashView.layout((int) x, (int) (y + 1), (int) width, (int) height - 1);
+ mSplashView.draw(canvas);
+ }
+ }
}
public TaskView getTaskView() {
@@ -326,6 +368,78 @@
}
/**
+ * Determine if the splash should be shown over top of the thumbnail.
+ *
+ * <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be
+ * different from the task.
+ */
+ boolean shouldShowSplashView() {
+ return isThumbnailAspectRatioDifferentFromThumbnailData()
+ || isThumbnailRotationDifferentFromTask();
+ }
+
+ private void updateSplashView(Drawable icon) {
+ if (icon == null || icon.getConstantState() == null) {
+ return;
+ }
+ mSplashViewDrawable = icon.getConstantState().newDrawable().mutate();
+ mSplashViewDrawable.setAlpha(mSplashAlpha);
+ ImageView imageView = mSplashView == null ? new ImageView(getContext()) : mSplashView;
+ imageView.setImageDrawable(mSplashViewDrawable);
+
+ imageView.setScaleType(ImageView.ScaleType.MATRIX);
+ Matrix matrix = new Matrix();
+
+ float drawableWidth = mSplashViewDrawable.getIntrinsicWidth();
+ float drawableHeight = mSplashViewDrawable.getIntrinsicHeight();
+ float viewWidth = getMeasuredWidth();
+ float viewCenterX = viewWidth / 2f;
+ float viewHeight = getMeasuredHeight();
+ float viewCenterY = viewHeight / 2f;
+ float centeredDrawableLeft = (viewWidth - drawableWidth) / 2f;
+ float centeredDrawableTop = (viewHeight - drawableHeight) / 2f;
+ float nonGridScale = getTaskView() == null ? 1 : 1 / getTaskView().getNonGridScale();
+ float recentsMaxScale = getTaskView() == null || getTaskView().getRecentsView() == null
+ ? 1 : 1 / getTaskView().getRecentsView().getMaxScaleForFullScreen();
+ float scale = nonGridScale * recentsMaxScale;
+
+ // Center the image in the view.
+ matrix.setTranslate(centeredDrawableLeft, centeredDrawableTop);
+ // Apply scale transformation after translation, pivoting around center of view.
+ matrix.postScale(scale, scale, viewCenterX, viewCenterY);
+
+ imageView.setImageMatrix(matrix);
+ mSplashView = imageView;
+ }
+
+ private boolean isThumbnailAspectRatioDifferentFromThumbnailData() {
+ if (mThumbnailData == null || mThumbnailData.thumbnail == null) {
+ return false;
+ }
+
+ float thumbnailViewAspect = getWidth() / (float) getHeight();
+ float thumbnailDataAspect =
+ mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
+
+ return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
+ thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
+ }
+
+ private boolean isThumbnailRotationDifferentFromTask() {
+ RecentsView recents = getTaskView().getRecentsView();
+ if (recents == null || mThumbnailData == null) {
+ return false;
+ }
+
+ if (recents.getPagedOrientationHandler() == PagedOrientationHandler.PORTRAIT) {
+ int currentRotation = recents.getPagedViewOrientedState().getRecentsActivityRotation();
+ return (currentRotation - mThumbnailData.rotation) % 2 != 0;
+ } else {
+ return recents.getPagedOrientationHandler().getRotation() != mThumbnailData.rotation;
+ }
+ }
+
+ /**
* Potentially re-init the task overlay. Be cautious when calling this as the overlay may
* do processing on initialization.
*/
@@ -433,27 +547,17 @@
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
RectF thumbnailClipHint = new RectF();
- if (TaskView.clipLeft(dp)) {
- thumbnailClipHint.left = thumbnailData.insets.left;
- }
- if (TaskView.clipRight(dp)) {
- thumbnailClipHint.right = thumbnailData.insets.right;
- }
- if (TaskView.clipTop(dp)) {
- thumbnailClipHint.top = thumbnailData.insets.top;
- }
- if (TaskView.clipBottom(dp)) {
- thumbnailClipHint.bottom = thumbnailData.insets.bottom;
- }
+ float canvasScreenRatio = canvasWidth / (float) dp.widthPx;
+ float scaledTaskbarSize = dp.taskbarSize * canvasScreenRatio;
+ thumbnailClipHint.bottom = dp.isTablet ? scaledTaskbarSize : 0;
float scale = thumbnailData.scale;
final float thumbnailScale;
// Landscape vs portrait change.
// Note: Disable rotation in grid layout.
- boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
- && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN
- && !dp.isTablet;
+ boolean windowingModeSupportsRotation =
+ thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !dp.isTablet;
isOrientationDifferent = isOrientationChange(deltaRotate)
&& windowingModeSupportsRotation;
if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
@@ -475,8 +579,9 @@
float availableAspect = isRotated
? availableHeight / availableWidth
: availableWidth / availableHeight;
- boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan(
- canvasAspect, availableAspect, 0.1f);
+ boolean isAspectLargelyDifferent =
+ Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
+ availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
if (isRotated && isAspectLargelyDifferent) {
// Do not rotate thumbnail if it would not improve fit
isRotated = false;
@@ -485,18 +590,10 @@
if (isAspectLargelyDifferent) {
// Crop letterbox insets if insets isn't already clipped
- if (!TaskView.clipLeft(dp)) {
- thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
- }
- if (!TaskView.clipRight(dp)) {
- thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
- }
- if (!TaskView.clipTop(dp)) {
- thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
- }
- if (!TaskView.clipBottom(dp)) {
- thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
- }
+ thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
+ thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
+ thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
+ thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
availableWidth = surfaceWidth
- (thumbnailClipHint.left + thumbnailClipHint.right);
availableHeight = surfaceHeight
@@ -559,44 +656,15 @@
thumbnailScale = targetW / (croppedWidth * scale);
}
- Rect splitScreenInsets = dp.getInsets();
if (!isRotated) {
- // No Rotation
- if (dp.isMultiWindowMode) {
- mClippedInsets.offsetTo(splitScreenInsets.left * scale,
- splitScreenInsets.top * scale);
- } else {
- mClippedInsets.offsetTo(thumbnailClipHint.left * scale,
- thumbnailClipHint.top * scale);
- }
mMatrix.setTranslate(
-thumbnailClipHint.left * scale,
-thumbnailClipHint.top * scale);
} else {
- setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds, dp);
+ setThumbnailRotation(deltaRotate, thumbnailBounds);
}
- final float widthWithInsets;
- final float heightWithInsets;
- if (isOrientationDifferent) {
- widthWithInsets = thumbnailBounds.height() * thumbnailScale;
- heightWithInsets = thumbnailBounds.width() * thumbnailScale;
- } else {
- widthWithInsets = thumbnailBounds.width() * thumbnailScale;
- heightWithInsets = thumbnailBounds.height() * thumbnailScale;
- }
- mClippedInsets.left *= thumbnailScale;
- mClippedInsets.top *= thumbnailScale;
-
- if (dp.isMultiWindowMode) {
- mClippedInsets.right = splitScreenInsets.right * scale * thumbnailScale;
- mClippedInsets.bottom = splitScreenInsets.bottom * scale * thumbnailScale;
- } else {
- mClippedInsets.right = Math.max(0,
- widthWithInsets - mClippedInsets.left - canvasWidth);
- mClippedInsets.bottom = Math.max(0,
- heightWithInsets - mClippedInsets.top - canvasHeight);
- }
+ mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
mMatrix.postScale(thumbnailScale, thumbnailScale);
mIsOrientationChanged = isOrientationDifferent;
@@ -617,44 +685,32 @@
return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
}
- private void setThumbnailRotation(int deltaRotate, RectF thumbnailInsets, float scale,
- Rect thumbnailPosition, DeviceProfile dp) {
- float newLeftInset = 0;
- float newTopInset = 0;
+ private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
float translateX = 0;
float translateY = 0;
mMatrix.setRotate(90 * deltaRotate);
switch (deltaRotate) { /* Counter-clockwise */
case Surface.ROTATION_90:
- newLeftInset = thumbnailInsets.bottom;
- newTopInset = thumbnailInsets.left;
translateX = thumbnailPosition.height();
break;
case Surface.ROTATION_270:
- newLeftInset = thumbnailInsets.top;
- newTopInset = thumbnailInsets.right;
translateY = thumbnailPosition.width();
break;
case Surface.ROTATION_180:
- newLeftInset = -thumbnailInsets.top;
- newTopInset = -thumbnailInsets.left;
translateX = thumbnailPosition.width();
translateY = thumbnailPosition.height();
break;
}
- mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
mMatrix.postTranslate(translateX, translateY);
- if (TaskView.useFullThumbnail(dp)) {
- mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
- }
}
/**
* Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
*/
public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) {
- return TaskView.useFullThumbnail(dp) ? mClippedInsets : EMPTY_RECT_F;
+ return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
+ ? mClippedInsets : EMPTY_RECT_F;
}
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d58bb7c..5602d88 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,8 +18,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
-import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
@@ -43,7 +43,6 @@
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Outline;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -57,7 +56,6 @@
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -67,7 +65,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -89,7 +86,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
@@ -139,41 +135,6 @@
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
- /**
- * Should the TaskView display clip off the left inset in RecentsView.
- */
- public static boolean clipLeft(DeviceProfile deviceProfile) {
- return false;
- }
-
- /**
- * Should the TaskView display clip off the top inset in RecentsView.
- */
- public static boolean clipTop(DeviceProfile deviceProfile) {
- return false;
- }
-
- /**
- * Should the TaskView display clip off the right inset in RecentsView.
- */
- public static boolean clipRight(DeviceProfile deviceProfile) {
- return false;
- }
-
- /**
- * Should the TaskView display clip off the bottom inset in RecentsView.
- */
- public static boolean clipBottom(DeviceProfile deviceProfile) {
- return deviceProfile.isTablet;
- }
-
- /**
- * Should the TaskView scale down to fit whole thumbnail in fullscreen.
- */
- public static boolean useFullThumbnail(DeviceProfile deviceProfile) {
- return deviceProfile.isTablet && !deviceProfile.isTaskbarPresentInApps;
- }
-
private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
@@ -362,8 +323,6 @@
}
};
- private final TaskOutlineProvider mOutlineProvider;
-
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
@@ -371,6 +330,7 @@
protected final DigitalWellBeingToast mDigitalWellBeingToast;
private float mFullscreenProgress;
private float mGridProgress;
+ protected float mOverviewProgress;
private float mNonGridScale = 1;
private float mDismissScale = 1;
protected final FullscreenDrawParams mCurrentFullscreenParams;
@@ -430,7 +390,6 @@
private boolean mIsClickableAsLiveTile = true;
-
public TaskView(Context context) {
this(context, null);
}
@@ -446,10 +405,6 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
-
- mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams,
- mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
- setOutlineProvider(mOutlineProvider);
}
public void setTaskViewId(int id) {
@@ -698,6 +653,9 @@
if (freezeTaskList) {
ActivityOptionsCompat.setFreezeRecentTasksList(opts);
}
+ // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
+ opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
+ ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle());
Task.TaskKey key = mTask.key;
UI_HELPER_EXECUTOR.execute(() -> {
if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
@@ -726,10 +684,6 @@
return;
}
- // Reset the minimized state since we force-toggled the minimized state when entering
- // overview, but never actually finished the recents animation
- SystemUiProxy.INSTANCE.get(getContext()).setSplitScreenMinimized(false);
-
mIsClickableAsLiveTile = false;
RemoteAnimationTargets targets;
if (remoteTargetHandles.length == 1) {
@@ -1102,6 +1056,21 @@
return scale;
}
+ /**
+ * Updates progress of task view for entering/exiting overview on swipe up/down.
+ *
+ * <p>Updates the alpha of any splash screen over the thumbnail if it exists.
+ */
+ public void setOverviewProgress(float overviewProgress) {
+ mOverviewProgress = overviewProgress;
+ applyThumbnailSplashAlpha();
+ }
+
+ protected void applyThumbnailSplashAlpha() {
+ mSnapshotView.setSplashAlpha(
+ Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+ }
+
private void setSplitSelectTranslationX(float x) {
mSplitSelectTranslationX = x;
applyTranslationX();
@@ -1289,33 +1258,6 @@
mEndQuickswitchCuj = endQuickswitchCuj;
}
- private static final class TaskOutlineProvider extends ViewOutlineProvider {
-
- private int mMarginTop;
- private FullscreenDrawParams mFullscreenParams;
-
- TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams, int topMargin) {
- mMarginTop = topMargin;
- mFullscreenParams = fullscreenParams;
- }
-
- public void updateParams(FullscreenDrawParams params, int topMargin) {
- mFullscreenParams = params;
- mMarginTop = topMargin;
- }
-
- @Override
- public void getOutline(View view, Outline outline) {
- RectF insets = mFullscreenParams.mCurrentDrawnInsets;
- float scale = mFullscreenParams.mScale;
- outline.setRoundRect(0,
- (int) (mMarginTop * scale),
- (int) ((insets.left + view.getWidth() + insets.right) * scale),
- (int) ((insets.top + view.getHeight() + insets.bottom) * scale),
- mFullscreenParams.mCurrentDrawnCornerRadius);
- }
- }
-
private int getExpectedViewHeight(View view) {
int expectedHeight;
int h = view.getLayoutParams().height;
@@ -1343,7 +1285,7 @@
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile(), taskContainer)) {
+ taskContainer)) {
info.addAction(s.createAccessibilityAction(context));
}
}
@@ -1381,7 +1323,7 @@
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile(), taskContainer)) {
+ taskContainer)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
return true;
@@ -1421,11 +1363,6 @@
mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
updateSnapshotRadius();
-
- mOutlineProvider.updateParams(
- mCurrentFullscreenParams,
- mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
- invalidateOutline();
}
protected void updateSnapshotRadius() {
@@ -1537,7 +1474,6 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
}
@@ -1584,17 +1520,14 @@
RectF insets = pph.getInsetsToDrawInFullscreen(dp);
float currentInsetsLeft = insets.left * fullscreenProgress;
+ float currentInsetsTop = insets.top * fullscreenProgress;
float currentInsetsRight = insets.right * fullscreenProgress;
- float insetsBottom = insets.bottom;
- if (dp.isTaskbarPresentInApps) {
- insetsBottom = Math.max(0, insetsBottom - dp.taskbarSize);
- }
- mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
- currentInsetsRight, insetsBottom * fullscreenProgress);
- float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
+ float currentInsetsBottom = insets.bottom * fullscreenProgress;
+ mCurrentDrawnInsets.set(
+ currentInsetsLeft, currentInsetsTop, currentInsetsRight, currentInsetsBottom);
mCurrentDrawnCornerRadius =
- Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius)
+ Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
/ parentScale / taskViewScale;
// We scaled the thumbnail to fit the content (excluding insets) within task view width.
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index d8be307..4eec319 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.when;
import android.os.Handler;
+import android.view.View;
import androidx.test.runner.AndroidJUnit4;
@@ -58,6 +59,8 @@
TaskbarControllers mockTaskbarControllers;
@Mock
TaskbarActivityContext mockTaskbarActivityContext;
+ @Mock
+ View mockView;
private TaskbarNavButtonController mNavButtonController;
@@ -76,110 +79,110 @@
@Test
public void testPressBack() {
- mNavButtonController.onButtonClick(BUTTON_BACK);
+ mNavButtonController.onButtonClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(1)).onBackPressed();
}
@Test
public void testPressImeSwitcher() {
- mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
+ mNavButtonController.onButtonClick(BUTTON_IME_SWITCH, mockView);
verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
}
@Test
public void testPressA11yShortClick() {
- mNavButtonController.onButtonClick(BUTTON_A11Y);
+ mNavButtonController.onButtonClick(BUTTON_A11Y, mockView);
verify(mockSystemUiProxy, times(1))
.notifyAccessibilityButtonClicked(DISPLAY_ID);
}
@Test
public void testPressA11yLongClick() {
- mNavButtonController.onButtonLongClick(BUTTON_A11Y);
+ mNavButtonController.onButtonLongClick(BUTTON_A11Y, mockView);
verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
}
@Test
public void testLongPressHome() {
- mNavButtonController.onButtonLongClick(BUTTON_HOME);
+ mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
verify(mockSystemUiProxy, times(1)).startAssistant(any());
}
@Test
public void testPressHome() {
- mNavButtonController.onButtonClick(BUTTON_HOME);
+ mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
}
@Test
public void testPressRecents() {
- mNavButtonController.onButtonClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
}
@Test
public void testPressRecentsWithScreenPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
- mNavButtonController.onButtonClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
}
@Test
public void testLongPressBackRecentsNotPinned() {
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsTooLongPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
try {
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsMultipleAttemptPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
try {
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
// Try again w/in threshold
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
}
@Test
public void testLongPressHomeScreenPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
- mNavButtonController.onButtonLongClick(BUTTON_HOME);
+ mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
verify(mockSystemUiProxy, times(0)).startAssistant(any());
}
@Test
public void testNoCallsToNullLogger() {
- mNavButtonController.onButtonClick(BUTTON_HOME);
+ mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
verify(mockStatsLogManager, times(0)).logger();
verify(mockStatsLogger, times(0)).log(any());
}
@@ -187,9 +190,9 @@
@Test
public void testNoCallsAfterNullingOut() {
mNavButtonController.init(mockTaskbarControllers);
- mNavButtonController.onButtonClick(BUTTON_HOME);
+ mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
mNavButtonController.onDestroy();
- mNavButtonController.onButtonClick(BUTTON_HOME);
+ mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
}
@@ -197,7 +200,7 @@
@Test
public void testLogOnTap() {
mNavButtonController.init(mockTaskbarControllers);
- mNavButtonController.onButtonClick(BUTTON_HOME);
+ mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
}
@@ -205,7 +208,7 @@
@Test
public void testLogOnLongpress() {
mNavButtonController.init(mockTaskbarControllers);
- mNavButtonController.onButtonLongClick(BUTTON_HOME);
+ mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
}
@@ -213,11 +216,11 @@
@Test
public void testBackOverviewLogOnLongpress() {
mNavButtonController.init(mockTaskbarControllers);
- mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS);
verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP);
- mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS);
verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
}
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
new file mode 100644
index 0000000..3daf81d
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for phone with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfilePhone3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForPhone(isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(343)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.getCellSize().x).isEqualTo(265)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.getCellSize().y).isEqualTo(552)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(343)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(232)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(669)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun hotseatBarBottomPaddingPx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(135)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1189)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(false)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(0)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(53)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(53)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(573)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2447)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1334)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(147)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(558)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
new file mode 100644
index 0000000..e588c71
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for phone.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfilePhoneTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForPhone()
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(343)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.getCellSize().x).isEqualTo(265)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.getCellSize().y).isEqualTo(552)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(343)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(232)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(669)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun hotseatBarBottomPaddingPx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(135)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1189)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(false)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(0)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(53)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(53)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(573)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2447)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1334)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(147)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(558)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
new file mode 100644
index 0000000..d0c9346
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in landscape with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletLandscape3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isLandscape = true, isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(294)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(558)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(294)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(294)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(199)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(228)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(true)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1237)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(19)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(19)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(93)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1407)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(2522)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(109)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(1305)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(566)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(566)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
new file mode 100644
index 0000000..456ed2c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in landscape.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletLandscapeTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isLandscape = true)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(294)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(558)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(294)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(294)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(199)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(228)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(503)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(true)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(-503)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(19)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(19)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(93)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1407)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(2522)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(109)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(301)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(301)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
new file mode 100644
index 0000000..eed3598
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in portrait with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletPortrait3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(382)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(294)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(482)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(382)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(257)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1216)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(29)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(29)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(238)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2222)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1542)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(528)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(528)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(502)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
new file mode 100644
index 0000000..4be3e45
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in portrait.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletPortraitTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet()
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(382)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(294)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(482)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(382)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(257)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(256)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1216)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(29)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(29)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(238)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2222)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1542)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(192)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(192)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
new file mode 100644
index 0000000..6728540
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in landscape with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelLandscape3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isLandscape = true, isTwoPanel = true, isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(260)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(259)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(260)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(260)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(175)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1039)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(43)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(43)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(285)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1215)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1237)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(2)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(864)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(864)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(566)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
new file mode 100644
index 0000000..ba3ef55
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in landscape.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelLandscapeTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isLandscape = true, isTwoPanel = true)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(260)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(259)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(260)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(260)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(175)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(73)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(1039)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(43)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(43)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(285)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1215)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(1237)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(2)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(761)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(761)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
new file mode 100644
index 0000000..a4b446a
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in portrait with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelPortrait3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isTwoPanel = true, isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(199)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(153)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(509)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(199)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(134)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(685)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(33)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(33)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(291)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2169)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(767)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(2)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(266)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(502)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(502)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
new file mode 100644
index 0000000..0c5968e
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in portrait.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelPortraitTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForTablet(isTwoPanel = true)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isTrue()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(199)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(153)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(509)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(199)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(600)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(134)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(2)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(685)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(true)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(120)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(33)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(33)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(291)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(96)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(88)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(40)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(2169)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(767)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(2)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(272)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(112)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(459)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(459)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
new file mode 100644
index 0000000..2bad6bb
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for landscape phone with vertical bar and 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileVerticalBar3ButtonTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForPhone(isVerticalBar = true, isGestureMode = false)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isFalse()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(210)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(675)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(321)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(0)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(260)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(304)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(336)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(0)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(2221)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(false)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(0)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(14)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(266)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(0)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1340)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(2840)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(true)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(147)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(225)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
new file mode 100644
index 0000000..6256a43
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for landscape phone with vertical bar.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileVerticalBarTest : DeviceProfileBaseTest() {
+
+ lateinit var dp: DeviceProfile
+
+ @Before
+ fun before() {
+ initializeVarsForPhone(isVerticalBar = true)
+ dp = newDP()
+ }
+
+ @Test
+ fun isScalableGrid() {
+ assertThat(dp.isScalableGrid).isFalse()
+ }
+
+ @Test
+ fun cellWidthPx() {
+ assertThat(dp.cellWidthPx).isEqualTo(210)
+ }
+
+ @Test
+ fun cellHeightPx() {
+ assertThat(dp.cellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun getCellSizeX() {
+ assertThat(dp.cellSize.x).isEqualTo(675)
+ }
+
+ @Test
+ fun getCellSizeY() {
+ assertThat(dp.cellSize.y).isEqualTo(321)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxX() {
+ assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutBorderSpacePxY() {
+ assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxLeft() {
+ assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxTop() {
+ assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxRight() {
+ assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
+ }
+
+ @Test
+ fun cellLayoutPaddingPxBottom() {
+ assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
+ }
+
+ @Test
+ fun iconSizePx() {
+ assertThat(dp.iconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun iconTextSizePx() {
+ assertThat(dp.iconTextSizePx).isEqualTo(0)
+ }
+
+ @Test
+ fun iconDrawablePaddingPx() {
+ assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellWidthPx() {
+ assertThat(dp.folderCellWidthPx).isEqualTo(260)
+ }
+
+ @Test
+ fun folderCellHeightPx() {
+ assertThat(dp.folderCellHeightPx).isEqualTo(304)
+ }
+
+ @Test
+ fun folderChildIconSizePx() {
+ assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun folderChildTextSizePx() {
+ assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun folderChildDrawablePaddingPx() {
+ assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpaceOriginalPx() {
+ assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxX() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+ }
+
+ @Test
+ fun folderCellLayoutBorderSpacePxY() {
+ assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+ }
+
+ @Test
+ fun bottomSheetTopPadding() {
+ assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
+ }
+
+ @Test
+ fun allAppsShiftRange() {
+ assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+ }
+
+ @Test
+ fun allAppsTopPadding() {
+ assertThat(dp.allAppsTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsIconSizePx() {
+ assertThat(dp.allAppsIconSizePx).isEqualTo(196)
+ }
+
+ @Test
+ fun allAppsIconTextSizePx() {
+ assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
+ }
+
+ @Test
+ fun allAppsIconDrawablePaddingPx() {
+ assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
+ }
+
+ @Test
+ fun allAppsCellHeightPx() {
+ assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
+ }
+
+ @Test
+ fun allAppsCellWidthPx() {
+ assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxX() {
+ assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
+ }
+
+ @Test
+ fun allAppsBorderSpacePxY() {
+ assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
+ }
+
+ @Test
+ fun numShownAllAppsColumns() {
+ assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightPadding() {
+ assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun allAppsLeftRightMargin() {
+ assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun hotseatBarSizePx() {
+ assertThat(dp.hotseatBarSizePx).isEqualTo(336)
+ }
+
+ @Test
+ fun hotseatCellHeightPx() {
+ assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+ }
+
+ @Test
+ fun hotseatBarBottomSpacePx() {
+ assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingStartPx() {
+ assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
+ }
+
+ @Test
+ fun hotseatBarSidePaddingEndPx() {
+ assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
+ }
+
+ @Test
+ fun hotseatQsbSpace() {
+ assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+ }
+
+ @Test
+ fun hotseatQsbHeight() {
+ assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+ }
+
+ @Test
+ fun springLoadedHotseatBarTopMarginPx() {
+ assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
+ }
+
+ @Test
+ fun numShownHotseatIcons() {
+ assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+ }
+
+ @Test
+ fun hotseatBorderSpace() {
+ assertThat(dp.hotseatBorderSpace).isEqualTo(0)
+ }
+
+ @Test
+ fun isQsbInline() {
+ assertThat(dp.isQsbInline).isEqualTo(false)
+ }
+
+ @Test
+ fun qsbWidth() {
+ assertThat(dp.qsbWidth).isEqualTo(2221)
+ }
+
+ @Test
+ fun isTaskbarPresent() {
+ assertThat(dp.isTaskbarPresent).isEqualTo(false)
+ }
+
+ @Test
+ fun isTaskbarPresentInApps() {
+ assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+ }
+
+ @Test
+ fun taskbarSize() {
+ assertThat(dp.taskbarSize).isEqualTo(0)
+ }
+
+ @Test
+ fun desiredWorkspaceHorizontalMarginPx() {
+ assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingLeft() {
+ assertThat(dp.workspacePadding.left).isEqualTo(14)
+ }
+
+ @Test
+ fun workspacePaddingTop() {
+ assertThat(dp.workspacePadding.top).isEqualTo(0)
+ }
+
+ @Test
+ fun workspacePaddingRight() {
+ assertThat(dp.workspacePadding.right).isEqualTo(266)
+ }
+
+ @Test
+ fun workspacePaddingBottom() {
+ assertThat(dp.workspacePadding.bottom).isEqualTo(0)
+ }
+
+ @Test
+ fun iconScale() {
+ assertThat(dp.iconScale).isEqualTo(1)
+ }
+
+ @Test
+ fun cellScaleToFit() {
+ assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun workspaceTopPadding() {
+ assertThat(dp.workspaceTopPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun workspaceBottomPadding() {
+ assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskMarginPx() {
+ assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewTaskMarginGridPx() {
+ assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskIconSizePx() {
+ assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizePx() {
+ assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+ }
+
+ @Test
+ fun overviewTaskIconDrawableSizeGridPx() {
+ assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+ }
+
+ @Test
+ fun overviewTaskThumbnailTopMarginPx() {
+ assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+ }
+
+ @Test
+ fun overviewActionsTopMarginPx() {
+ assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
+ }
+
+ @Test
+ fun overviewActionsHeight() {
+ assertThat(dp.overviewActionsHeight).isEqualTo(168)
+ }
+
+ @Test
+ fun overviewActionsButtonSpacing() {
+ assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+ }
+
+ @Test
+ fun overviewPageSpacing() {
+ assertThat(dp.overviewPageSpacing).isEqualTo(56)
+ }
+
+ @Test
+ fun overviewRowSpacing() {
+ assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+ }
+
+ @Test
+ fun overviewGridSideMargin() {
+ assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+ }
+
+ @Test
+ fun dropTargetBarTopMarginPx() {
+ assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
+ }
+
+ @Test
+ fun dropTargetBarSizePx() {
+ assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
+ }
+
+ @Test
+ fun dropTargetBarBottomMarginPx() {
+ assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
+ }
+
+ @Test
+ fun workspaceSpringLoadedMinNextPageVisiblePx() {
+ assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+ }
+
+ @Test
+ fun getWorkspaceSpringLoadScale() {
+ assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
+ }
+
+ @Test
+ fun getCellLayoutHeight() {
+ assertThat(dp.cellLayoutHeight).isEqualTo(1340)
+ }
+
+ @Test
+ fun getCellLayoutWidth() {
+ assertThat(dp.cellLayoutWidth).isEqualTo(2840)
+ }
+
+ @Test
+ fun getPanelCount() {
+ assertThat(dp.panelCount).isEqualTo(1)
+ }
+
+ @Test
+ fun isVerticalBarLayout() {
+ assertThat(dp.isVerticalBarLayout).isEqualTo(true)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkTop() {
+ assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
+ }
+
+ @Test
+ fun getCellLayoutSpringLoadShrunkBottom() {
+ assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
+ }
+
+ @Test
+ fun getQsbOffsetY() {
+ assertThat(dp.qsbOffsetY).isEqualTo(147)
+ }
+
+ @Test
+ fun getTaskbarOffsetY() {
+ assertThat(dp.taskbarOffsetY).isEqualTo(225)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingLeft() {
+ assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingTop() {
+ assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingRight() {
+ assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
+ }
+
+ @Test
+ fun getHotseatLayoutPaddingBottom() {
+ assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
+ }
+
+ @Test
+ fun hotseatBarEndOffset() {
+ assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 9e5d958..1c15e1e 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -35,7 +35,6 @@
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Size;
-import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -290,15 +289,17 @@
private DisplayController.Info createDisplayInfo(Size screenSize, int rotation) {
Point displaySize = new Point(screenSize.getWidth(), screenSize.getHeight());
RotationUtils.rotateSize(displaySize, rotation);
- CachedDisplayInfo cdi = new CachedDisplayInfo(displaySize, rotation);
- WindowBounds wm = new WindowBounds(
+ CachedDisplayInfo cachedDisplayInfo = new CachedDisplayInfo(displaySize, rotation);
+ WindowBounds windowBounds = new WindowBounds(
new Rect(0, 0, displaySize.x, displaySize.y),
new Rect());
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
- doReturn(cdi).when(wmProxy).getDisplayInfo(any(), any());
- doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
+ doReturn(cachedDisplayInfo).when(wmProxy).getDisplayInfo(any());
+ doReturn(windowBounds).when(wmProxy).getRealBounds(any(), any());
+ ArrayMap<CachedDisplayInfo, WindowBounds[]> internalDisplayBounds = new ArrayMap<>();
+ doReturn(internalDisplayBounds).when(wmProxy).estimateInternalDisplayBounds(any());
return new DisplayController.Info(
- getApplicationContext(), mock(Display.class), wmProxy, new ArrayMap<>());
+ getApplicationContext(), wmProxy, new ArrayMap<>());
}
private float generateTouchRegionHeight(Size screenSize, int rotation) {
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 6ec6269..401b967 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -16,8 +16,6 @@
package com.android.quickstep;
-import android.content.Intent;
-
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -43,7 +41,7 @@
// b/143488140
mLauncher.goHome();
// Start an activity where the gestures start.
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ startTestActivity(2);
}
private void runTest(String... eventSequence) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 4bf247c..81df3c0 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -179,6 +179,20 @@
actionsView.clickAndDismissScreenshot();
}
+ @Test
+ @PortraitLandscape
+ public void testSplitFromOverviewForTablet() {
+ assumeTrue(mLauncher.isTablet());
+
+ startTestActivity(2);
+ startTestActivity(3);
+
+ mLauncher.goHome().switchToOverview().getOverviewActions()
+ .clickSplit()
+ .getTestActivityTask(2)
+ .open();
+ }
+
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt b/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
new file mode 100644
index 0000000..cf3c8c9
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.graphics.Rect
+import android.graphics.RectF
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/**
+ * Test for TaskThumbnailView class.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TaskThumbnailViewTest : DeviceProfileBaseTest() {
+
+ private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
+
+ private val mPreviewPositionHelper = PreviewPositionHelper()
+
+ @Test
+ fun getInsetsToDrawInFullscreen_clipTaskbarSizeFromBottomForTablets() {
+ initializeVarsForTablet()
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = dp.widthPx / 2
+ val canvasHeight = dp.heightPx / 2
+ val currentRotation = 0
+ val isRtl = false
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp, currentRotation, isRtl)
+
+ val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize / 2f)
+ assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
+ .isEqualTo(expectedClippedInsets)
+ }
+
+ @Test
+ fun getInsetsToDrawInFullscreen_doNotClipTaskbarSizeFromBottomForPhones() {
+ initializeVarsForPhone()
+ val dp = newDP()
+ val previewRect = Rect(0, 0, 100, 100)
+ val canvasWidth = dp.widthPx / 2
+ val canvasHeight = dp.heightPx / 2
+ val currentRotation = 0
+ val isRtl = false
+
+ mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+ canvasHeight, dp, currentRotation, isRtl)
+
+ val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
+ assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
+ .isEqualTo(expectedClippedInsets)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 7d414f4..d43aafa 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -23,8 +23,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.ArrayMap;
-import android.util.Pair;
-import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -148,7 +146,7 @@
int rotation = mDisplaySize.x > mDisplaySize.y
? Surface.ROTATION_90 : Surface.ROTATION_0;
CachedDisplayInfo cdi =
- new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect());
+ new CachedDisplayInfo(mDisplaySize, rotation, new Rect());
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
@@ -164,15 +162,15 @@
}
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
- doReturn(cdi).when(wmProxy).getDisplayInfo(any(), any());
- doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
+ doReturn(cdi).when(wmProxy).getDisplayInfo(any());
+ doReturn(wm).when(wmProxy).getRealBounds(any(), any());
- ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache =
+ ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache =
new ArrayMap<>();
- perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds));
+ perDisplayBoundsCache.put(cdi.normalize(), allBounds);
DisplayController.Info mockInfo = new Info(
- helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache);
+ helper.sandboxContext, wmProxy, perDisplayBoundsCache);
DisplayController controller =
DisplayController.INSTANCE.get(helper.sandboxContext);
diff --git a/res/drawable/bg_work_apps_paused_action_button.xml b/res/drawable/bg_work_apps_paused_action_button.xml
new file mode 100644
index 0000000..74d4693
--- /dev/null
+++ b/res/drawable/bg_work_apps_paused_action_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/accent_ripple_color">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/rounded_button_radius" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorControlHighlight" />
+ <corners android:radius="@dimen/rounded_button_radius" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml
index a47c8fe..6ad6c82 100644
--- a/res/drawable/work_apps_toggle_background.xml
+++ b/res/drawable/work_apps_toggle_background.xml
@@ -13,16 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false">
- <shape android:shape="rectangle">
- <corners android:radius="@dimen/work_fab_radius" />
- <solid android:color="?android:attr/colorControlHighlight" />
- <padding
- android:left="@dimen/work_profile_footer_padding"
- android:right="@dimen/work_profile_footer_padding" />
- </shape>
- </item>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/accent_ripple_color">
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/work_fab_radius" />
@@ -32,4 +24,4 @@
android:right="@dimen/work_profile_footer_padding" />
</shape>
</item>
-</selector>
+</ripple>
diff --git a/res/layout/all_apps_bottom_sheet_background.xml b/res/layout/all_apps_bottom_sheet_background.xml
index 12b6b7b..3e47690 100644
--- a/res/layout/all_apps_bottom_sheet_background.xml
+++ b/res/layout/all_apps_bottom_sheet_background.xml
@@ -22,7 +22,7 @@
<View
android:id="@+id/bottom_sheet_handle_area"
android:layout_width="match_parent"
- android:layout_height="36dp" />
+ android:layout_height="@dimen/bottom_sheet_handle_area_height" />
<View
android:id="@+id/bottom_sheet_handle"
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index dfe226a..098c9b0 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -16,37 +16,35 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <com.android.launcher3.workprofile.PersonalWorkPagedView
+ <com.android.launcher3.widget.picker.WidgetPagedView
android:id="@+id/widgets_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:layout_below="@id/collapse_handle"
android:descendantFocusability="afterDescendants"
- launcher:pageIndicator="@+id/tabs">
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+ launcher:pageIndicator="@+id/tabs" >
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/primary_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/work_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
- </com.android.launcher3.workprofile.PersonalWorkPagedView>
+ </com.android.launcher3.widget.picker.WidgetPagedView>
<!-- SearchAndRecommendationsView contains the tab layout as well -->
- <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+ <com.android.launcher3.views.StickyHeaderLayout
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:layout_below="@id/collapse_handle"
android:paddingBottom="0dp"
android:orientation="vertical">
@@ -58,6 +56,7 @@
android:gravity="center_horizontal"
android:textSize="24sp"
android:layout_marginTop="24dp"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:textColor="?android:attr/textColorSecondary"
android:text="@string/widget_button_text"/>
@@ -68,7 +67,8 @@
android:elevation="0.1dp"
android:background="?android:attr/colorBackground"
android:paddingBottom="8dp"
- android:clipToPadding="false">
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+ launcher:layout_sticky="true">
<include layout="@layout/widgets_search_bar" />
</FrameLayout>
@@ -79,6 +79,7 @@
android:layout_marginTop="8dp"
android:background="@drawable/widgets_recommendation_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone" />
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
@@ -91,7 +92,8 @@
android:paddingLeft="@dimen/widget_tabs_horizontal_padding"
android:paddingRight="@dimen/widget_tabs_horizontal_padding"
android:background="?android:attr/colorBackground"
- style="@style/TextHeadline">
+ style="@style/TextHeadline"
+ launcher:layout_sticky="true">
<Button
android:id="@+id/tab_personal"
@@ -120,5 +122,5 @@
style="?android:attr/borderlessButtonStyle" />
</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
- </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
+ </com.android.launcher3.views.StickyHeaderLayout>
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 6a5d6cb..9da3e87 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -13,21 +13,21 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto" >
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/primary_widgets_list_view"
android:layout_below="@id/collapse_handle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<!-- SearchAndRecommendationsView without the tab layout as well -->
- <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+ <com.android.launcher3.views.StickyHeaderLayout
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:layout_below="@id/collapse_handle"
android:paddingBottom="16dp"
android:orientation="vertical">
@@ -40,6 +40,7 @@
android:textSize="24sp"
android:layout_marginTop="24dp"
android:textColor="?android:attr/textColorSecondary"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:text="@string/widget_button_text"/>
<FrameLayout
@@ -48,8 +49,10 @@
android:layout_height="wrap_content"
android:elevation="0.1dp"
android:background="?android:attr/colorBackground"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:paddingBottom="8dp"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ launcher:layout_sticky="true" >
<include layout="@layout/widgets_search_bar" />
</FrameLayout>
@@ -60,7 +63,8 @@
android:layout_marginTop="8dp"
android:background="@drawable/widgets_recommendation_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone" />
- </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
+ </com.android.launcher3.views.StickyHeaderLayout>
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 3cdc2e8..35bea27 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -19,7 +19,6 @@
android:id="@+id/widgets_list_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
android:orientation="horizontal"
android:importantForAccessibility="yes"
android:focusable="true"
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 79bce70..f614d9b 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -48,7 +48,7 @@
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_enable_btn_text"
android:textAlignment="center"
- android:background="@drawable/rounded_action_button"
+ android:background="@drawable/bg_work_apps_paused_action_button"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textSize="14sp" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 55139b3..445bfda 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item is verwyder"</string>
<string name="undo" msgid="4151576204245173321">"Ontdoen"</string>
<string name="action_move" msgid="4339390619886385032">"Skuif item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Skuif na posisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Skuif na gunstelingposisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item geskuif"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index ec69acb..474cd9f 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ንጥል ነገር ተንቀሳቅሷል"</string>
<string name="undo" msgid="4151576204245173321">"ቀልብስ"</string>
<string name="action_move" msgid="4339390619886385032">"ንጥልን አንቀሳቅስ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> አንቀሳቅስ"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"በ<xliff:g id="STRING">%3$s</xliff:g> ውስጥ ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> ይውሰዱ"</string>
<string name="move_to_position" msgid="6750008980455459790">"ወደ አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ወደ ተወዳጆች አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string>
<string name="item_moved" msgid="4606538322571412879">"ንጥል ተንቀሳቅሷል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 0e48179..a4f7c03 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string>
<string name="undo" msgid="4151576204245173321">"تراجع"</string>
<string name="action_move" msgid="4339390619886385032">"نقل العنصر"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"نقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g> في <xliff:g id="STRING">%3$s</xliff:g>."</string>
<string name="move_to_position" msgid="6750008980455459790">"نقل إلى الموضع <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"نقل إلى الموضع المفضل <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"تم نقل العنصر"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 5fc8ca6..beb9619 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"বস্তুটো আঁতৰোৱা হ’ল"</string>
<string name="undo" msgid="4151576204245173321">"আনডু কৰক"</string>
<string name="action_move" msgid="4339390619886385032">"বস্তু স্থানান্তৰ কৰক"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"শাৰী <xliff:g id="NUMBER_0">%1$s</xliff:g> স্তম্ভ <xliff:g id="NUMBER_1">%2$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ত <xliff:g id="NUMBER_0">%1$s</xliff:g> নম্বৰ শাৰী <xliff:g id="NUMBER_1">%2$s</xliff:g> নম্বৰ স্তম্ভলৈ লৈ যাওক"</string>
<string name="move_to_position" msgid="6750008980455459790">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
<string name="item_moved" msgid="4606538322571412879">"বস্তুটো স্থানান্তৰ কৰা হ’ল"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index ad72424..93e6f08 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
<string name="undo" msgid="4151576204245173321">"Ləğv edin"</string>
<string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="NUMBER_0">%1$s</xliff:g> saylı sətir, <xliff:g id="NUMBER_1">%2$s</xliff:g> saylı sütuna (<xliff:g id="STRING">%3$s</xliff:g>) köçürün"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> sevimlilər mövqeyinə köçürün"</string>
<string name="item_moved" msgid="4606538322571412879">"Elementin yeri dəyişildi"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d7e85d9..ded4dc9 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Opozovi"</string>
<string name="action_move" msgid="4339390619886385032">"Premesti stavku"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Premesti u red <xliff:g id="NUMBER_0">%1$s</xliff:g> i kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premestite u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju u omiljenim"</string>
<string name="item_moved" msgid="4606538322571412879">"Stavka je premeštena"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index a845f9a..7fa8341 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Элемент выдалены"</string>
<string name="undo" msgid="4151576204245173321">"Адрабіць"</string>
<string name="action_move" msgid="4339390619886385032">"Перамясціць элемент"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Перамясціць у радок <xliff:g id="NUMBER_0">%1$s</xliff:g> слупок <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перайсці да радка <xliff:g id="NUMBER_0">%1$s</xliff:g> у слупку <xliff:g id="NUMBER_1">%2$s</xliff:g> на старонцы <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Перамясціць у абранае, у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Элемент перамешчаны"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 0c7fcd3..62b7796 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Елементът е премахнат"</string>
<string name="undo" msgid="4151576204245173321">"Отмяна"</string>
<string name="action_move" msgid="4339390619886385032">"Преместване на елемента"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Преместване към ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместване на <xliff:g id="STRING">%3$s</xliff:g> – ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g> в любимите"</string>
<string name="item_moved" msgid="4606538322571412879">"Елементът е преместен"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 5c8c342..1be272b 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"আইটেম সরানো হয়েছে"</string>
<string name="undo" msgid="4151576204245173321">"ফিরিয়ে আনুন"</string>
<string name="action_move" msgid="4339390619886385032">"আইটেম সরান"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> এ সরান"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>-এ সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> সরান"</string>
<string name="move_to_position" msgid="6750008980455459790">"অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দসই অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"আইটেম সরানো হয়েছে"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 173518f..639b6b3 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Poništi"</string>
<string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premještanje u red <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string>
<string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index e6a4af2..ee35fd3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"S\'ha suprimit l\'element"</string>
<string name="undo" msgid="4151576204245173321">"Desfés"</string>
<string name="action_move" msgid="4339390619886385032">"Desplaça l\'element"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Desplaça l\'element a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g> i la columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mou a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> a <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Desplaça l\'element a la posició <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Desplaça l\'element a la posició de preferits <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Element desplaçat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 8b3ef2d..1b418c6 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Položka byla odstraněna"</string>
<string name="undo" msgid="4151576204245173321">"Zpět"</string>
<string name="action_move" msgid="4339390619886385032">"Přesunout položku"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g> do sloupce <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g>, sloupec <xliff:g id="NUMBER_1">%2$s</xliff:g> na ploše <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Přesunout na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Přesunout do oblíbených položek na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Položka byla přesunuta"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8160465..5cdadcd 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
<string name="undo" msgid="4151576204245173321">"Fortryd"</string>
<string name="action_move" msgid="4339390619886385032">"Flyt element"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Flyt til position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Flyt til foretrukne position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Elementet blev flyttet"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 912e7fa..b75ef9d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element entfernt"</string>
<string name="undo" msgid="4151576204245173321">"Rückgängig"</string>
<string name="action_move" msgid="4339390619886385032">"Element verschieben"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"In Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g>, Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> verschoben"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"In <xliff:g id="STRING">%3$s</xliff:g> zu Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g> Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> bewegen"</string>
<string name="move_to_position" msgid="6750008980455459790">"Auf Position <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Auf Favoritenposition <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string>
<string name="item_moved" msgid="4606538322571412879">"Element verschoben"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 0f613de..99c9e23 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Το στοιχείο καταργήθηκε"</string>
<string name="undo" msgid="4151576204245173321">"Αναίρεση"</string>
<string name="action_move" msgid="4339390619886385032">"Μετακίνηση στοιχείου"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g>, στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g> στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g> στην <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g> στα αγαπημένα"</string>
<string name="item_moved" msgid="4606538322571412879">"Το στοιχείο καταργήθηκε"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index d6951ad..4977beb 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removed"</string>
<string name="undo" msgid="4151576204245173321">"Undo"</string>
<string name="action_move" msgid="4339390619886385032">"Move item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favorites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3897974..3ed312b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Se eliminó el elemento."</string>
<string name="undo" msgid="4151576204245173321">"Deshacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición de favoritos número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 823e5a7..567ff0d 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elemento quitado"</string>
<string name="undo" msgid="4151576204245173321">"Deshacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g> de favoritos"</string>
<string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index bd2190a..c34a003 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
<string name="undo" msgid="4151576204245173321">"Võta tagasi"</string>
<string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Teisalda reale <xliff:g id="NUMBER_0">%1$s</xliff:g> veerus <xliff:g id="NUMBER_1">%2$s</xliff:g> kohas <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Teisaldamine lemmikute <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
<string name="item_moved" msgid="4606538322571412879">"Üksus teisaldati"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index c1eaea8..55e0c3c 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
<string name="undo" msgid="4151576204245173321">"Desegin"</string>
<string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Joan <xliff:g id="NUMBER_0">%1$s</xliff:g>garren errenkadako <xliff:g id="NUMBER_1">%2$s</xliff:g>garren zutabera, <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Eraman gogokoen <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
<string name="item_moved" msgid="4606538322571412879">"Elementua mugitu da"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index d23eac2..12db5bd 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"مورد حذف شد"</string>
<string name="undo" msgid="4151576204245173321">"واگرد"</string>
<string name="action_move" msgid="4339390619886385032">"انتقال مورد"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"انتقال به سطر <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقال به ردیف <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g> در <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"انتقال به موقعیت <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"انتقال به موقعیت دلخواه <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"مورد منتقل شد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index ccc22f0..135c452 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Kohde poistettiin"</string>
<string name="undo" msgid="4151576204245173321">"Kumoa"</string>
<string name="action_move" msgid="4339390619886385032">"Siirrä kohde"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Siirrä rivin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Siirrä riviin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g> näytöllä <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Siirrä kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Siirrä suosikkien kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
<string name="item_moved" msgid="4606538322571412879">"Kohde on siirretty."</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 11bcae1..446a507 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Élément retiré"</string>
<string name="undo" msgid="4151576204245173321">"Annuler"</string>
<string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers rangée <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer à la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string>
<string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 9b30b1e..1d623b1 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Élément supprimé"</string>
<string name="undo" msgid="4151576204245173321">"Annuler"</string>
<string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string>
<string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 013011a..256c4d3 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Quitouse o elemento"</string>
<string name="undo" msgid="4151576204245173321">"Desfacer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g> columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> de <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover á posición dos favoritos <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Moveuse o elemento"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 2d504e3..f1e1996 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"આઇટમ કાઢી નાખી"</string>
<string name="undo" msgid="4151576204245173321">"રદ કરો"</string>
<string name="action_move" msgid="4339390619886385032">"આઇટમ ખસેડો"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> પંક્તિ <xliff:g id="NUMBER_1">%2$s</xliff:g> કૉલમ પર ખસેડો"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>માં પંક્તિ <xliff:g id="NUMBER_0">%1$s</xliff:g> કૉલમ <xliff:g id="NUMBER_1">%2$s</xliff:g> પર આ આઇટમને ખસેડો"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> સ્થિતિ પર ખસેડો"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"મનપસંદ સ્થિતિ <xliff:g id="NUMBER">%1$s</xliff:g> પર ખસેડો"</string>
<string name="item_moved" msgid="4606538322571412879">"આઇટમ ખસેડી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index de6ad1c..1b05040 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"आइटम हटाया गया"</string>
<string name="undo" msgid="4151576204245173321">"पहले जैसा करें"</string>
<string name="action_move" msgid="4339390619886385032">"आइटम ले जाएं"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> पर ले जाएं"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> में, पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> कॉलम <xliff:g id="NUMBER_1">%2$s</xliff:g> पर जाएं"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थिति पर ले जाएं"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> की पसंदीदा स्थिति पर ले जाएं"</string>
<string name="item_moved" msgid="4606538322571412879">"आइटम ले जाया गया"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 0e58e53..4a0fbe8 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
<string name="undo" msgid="4151576204245173321">"Poništi"</string>
<string name="action_move" msgid="4339390619886385032">"Premještanje stavke"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Premještanje u redak <xliff:g id="NUMBER_0">%1$s</xliff:g>, stupac <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premjesti u redak <xliff:g id="NUMBER_0">%1$s</xliff:g> stupac <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Premještanje na položaj <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Premještanje na položaj favorita <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Stavka premještena"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 30095d7..c1e527d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elem eltávolítva"</string>
<string name="undo" msgid="4151576204245173321">"Mégse"</string>
<string name="action_move" msgid="4339390619886385032">"Elem mozgatása"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Áthelyezés ide: <xliff:g id="NUMBER_0">%1$s</xliff:g>. sor, <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlop"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Áthelyezés a(z) <xliff:g id="NUMBER_0">%1$s</xliff:g>. sorba és a(z) <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlopba itt: <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Áthelyezés a(z) <xliff:g id="NUMBER">%1$s</xliff:g>. pozícióba"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Áthelyezés a kedvencek <xliff:g id="NUMBER">%1$s</xliff:g>. pozíciójába"</string>
<string name="item_moved" msgid="4606538322571412879">"Elem áthelyezve"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 3b9761f..f0d01f1 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Տարրը հեռացվեց"</string>
<string name="undo" msgid="4151576204245173321">"Հետարկել"</string>
<string name="action_move" msgid="4339390619886385032">"Տեղափոխել տարրը"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Տեղափոխել տող <xliff:g id="NUMBER_0">%1$s</xliff:g> սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Տեղափոխել շարք <xliff:g id="NUMBER_0">%1$s</xliff:g>, սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g> (<xliff:g id="STRING">%3$s</xliff:g>)"</string>
<string name="move_to_position" msgid="6750008980455459790">"Տեղափոխել դիրք <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Տեղափոխել նախընտրած դիրք՝ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Տարրը տեղափոխվեց"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 02f4860..1a97a45 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item dihapus"</string>
<string name="undo" msgid="4151576204245173321">"Urungkan"</string>
<string name="action_move" msgid="4339390619886385032">"Pindahkan item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> di <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"PIndahkan ke posisi <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Pindahkan ke posisi favorit <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item dipindahkan"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 17cf2e4..a1959f8 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
<string name="undo" msgid="4151576204245173321">"Afturkalla"</string>
<string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Færðu þig í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g> í <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g> á festisvæði"</string>
<string name="item_moved" msgid="4606538322571412879">"Atriði fært"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index edff814..302efcb 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elemento rimosso"</string>
<string name="undo" msgid="4151576204245173321">"Annulla"</string>
<string name="action_move" msgid="4339390619886385032">"Sposta elemento"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Sposta a riga <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Spostati alla riga <xliff:g id="NUMBER_0">%1$s</xliff:g> colonna <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g> dei preferiti"</string>
<string name="item_moved" msgid="4606538322571412879">"Elemento spostato"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7de8f08..15e200f 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"הפריט הוסר"</string>
<string name="undo" msgid="4151576204245173321">"ביטול"</string>
<string name="action_move" msgid="4339390619886385032">"העברת הפריט"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"העברה אל שורה <xliff:g id="NUMBER_0">%1$s</xliff:g> עמודה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"צריך לעבור לשורה <xliff:g id="NUMBER_0">%1$s</xliff:g> ולטור <xliff:g id="NUMBER_1">%2$s</xliff:g> ב-<xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g> במועדפים"</string>
<string name="item_moved" msgid="4606538322571412879">"הפריט הועבר"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index d7cbb07..2fd3ab5 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"アイテムを削除しました"</string>
<string name="undo" msgid="4151576204245173321">"元に戻す"</string>
<string name="action_move" msgid="4339390619886385032">"アイテムを移動"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"行<xliff:g id="NUMBER_0">%1$s</xliff:g>、列<xliff:g id="NUMBER_1">%2$s</xliff:g>に移動"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> の行 <xliff:g id="NUMBER_0">%1$s</xliff:g>、列 <xliff:g id="NUMBER_1">%2$s</xliff:g> に移動します"</string>
<string name="move_to_position" msgid="6750008980455459790">"位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"お気に入りの位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string>
<string name="item_moved" msgid="4606538322571412879">"アイテムを移動しました"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index ba5be87..24f95dd 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ერთეული წაიშალა"</string>
<string name="undo" msgid="4151576204245173321">"მოქმედების გაუქმება"</string>
<string name="action_move" msgid="4339390619886385032">"ერთეულის გადაადგილება"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"გადატანა რიგში <xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტში <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"გადაიტანეთ მწკრივი #<xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტი #<xliff:g id="NUMBER_1">%2$s</xliff:g> <xliff:g id="STRING">%3$s</xliff:g>-ში"</string>
<string name="move_to_position" msgid="6750008980455459790">"გადატანა <xliff:g id="NUMBER">%1$s</xliff:g> პოზიციაზე"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"გადატანა რჩეულთა პოზიციაზე <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"ერთეული გადაადგილდა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 83b5a66..8ed7ec5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Элемент жойылды"</string>
<string name="undo" msgid="4151576204245173321">"Қайтару"</string>
<string name="action_move" msgid="4339390619886385032">"Элементті жылжыту"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>-жол, <xliff:g id="NUMBER_1">%2$s</xliff:g>-бағанға жылжыту"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> экранында <xliff:g id="NUMBER_0">%1$s</xliff:g> жолын, <xliff:g id="NUMBER_1">%2$s</xliff:g> бағанын жылжытыңыз."</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-орынға жылжыту"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> нөмірлі таңдаулы орынға жылжыту"</string>
<string name="item_moved" msgid="4606538322571412879">"Элемент жылжытылды"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 73ec4df..306449c 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"បានដកធាតុចេញ"</string>
<string name="undo" msgid="4151576204245173321">"ត្រឡប់វិញ"</string>
<string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ផ្លាស់ទីទៅជួរដេកទី <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរទី <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ផ្លាស់ទៅជួរដេក <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរ <xliff:g id="NUMBER_1">%2$s</xliff:g> ក្នុង <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"ផ្លាស់ទីទៅទីតាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ផ្លាស់ទីទៅការចូលចិត្តទីតាំងទី <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"បានផ្លាស់ទីធាតុ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 1e6f39e..a9b89b5 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="undo" msgid="4151576204245173321">"ರದ್ದುಮಾಡಿ"</string>
<string name="action_move" msgid="4339390619886385032">"ಐಟಂ ಸರಿಸಿ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> ಸಾಲು <xliff:g id="NUMBER_1">%2$s</xliff:g> ಕಾಲಮ್ಗೆ ಸರಿಸಿ"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ನಲ್ಲಿ ಸಾಲು <xliff:g id="NUMBER_0">%1$s</xliff:g> ಅನ್ನು ಕಾಲಮ್ <xliff:g id="NUMBER_1">%2$s</xliff:g> ಗೆ ಸರಿಸಿ"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ಮೆಚ್ಚಿನ <xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="item_moved" msgid="4606538322571412879">"ಐಟಂ ಸರಿಸಲಾಗಿದೆ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index d5ec664..0935aa1 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"항목 삭제됨"</string>
<string name="undo" msgid="4151576204245173321">"실행취소"</string>
<string name="action_move" msgid="4339390619886385032">"항목 이동"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>의 <xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"즐겨찾는 <xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string>
<string name="item_moved" msgid="4606538322571412879">"항목을 이동했습니다."</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index ea4a7d2..755e274 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Жоюлду"</string>
<string name="undo" msgid="4151576204245173321">"Кайтаруу"</string>
<string name="action_move" msgid="4339390619886385032">"Муну жылдыруу"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> катарга <xliff:g id="NUMBER_1">%2$s</xliff:g> тилкеге жылдыруу"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ичиндеги <xliff:g id="NUMBER_1">%2$s</xliff:g>-тилкенин <xliff:g id="NUMBER_0">%1$s</xliff:g>-cабына жылдыруу"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> орунга жылдыруу"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Тандалмаларга <xliff:g id="NUMBER">%1$s</xliff:g> жылдыруу"</string>
<string name="item_moved" msgid="4606538322571412879">"Нерсе жылдырылды"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 7291cf2..9e75492 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ເອົາລາຍການອອກໄປແລ້ວ"</string>
<string name="undo" msgid="4151576204245173321">"ຍົກເລີກ"</string>
<string name="action_move" msgid="4339390619886385032">"ຍ້າຍລາຍການ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ຍ້າຍໄປໃສ່ແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ຍ້າຍໄປແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g> ໃນ <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງທີ່ມັກ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"ຍ້າຍລາຍການແລ້ວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index ddff9d4..bbe0cfa 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elementas perkeltas"</string>
<string name="undo" msgid="4151576204245173321">"Anuliuoti"</string>
<string name="action_move" msgid="4339390619886385032">"Perkelti elementą"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutę, <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutės <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį, esantį <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> poziciją"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> mėgstamiausių poziciją"</string>
<string name="item_moved" msgid="4606538322571412879">"Elementas perkeltas"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index a5b7c66..b5836ef 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Vienums noņemts"</string>
<string name="undo" msgid="4151576204245173321">"Atsaukt"</string>
<string name="action_move" msgid="4339390619886385032">"Pārvietot vienumu"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Pārvietot uz <xliff:g id="NUMBER_0">%1$s</xliff:g>. rindu, <xliff:g id="NUMBER_1">%2$s</xliff:g>. kolonnu"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pārvietot uz rindu numur <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonnu numur <xliff:g id="NUMBER_1">%2$s</xliff:g> šajā ekrānā: <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. pozīciju"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. izlases pozīciju"</string>
<string name="item_moved" msgid="4606538322571412879">"Vienums pārvietots"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 32c3fc0..d452eba 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string>
<string name="undo" msgid="4151576204245173321">"Врати"</string>
<string name="action_move" msgid="4339390619886385032">"Премести ја ставката"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести во ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместете во редица <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g> во <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g> во омилени"</string>
<string name="item_moved" msgid="4606538322571412879">"Ставката е преместена"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index d25f9dd..df532c0 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ഇനം നീക്കംചെയ്തു"</string>
<string name="undo" msgid="4151576204245173321">"പഴയപടിയാക്കുക"</string>
<string name="action_move" msgid="4339390619886385032">"ഇനം നീക്കുക"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"വരി <xliff:g id="NUMBER_0">%1$s</xliff:g> നിര <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> എന്നതിലെ <xliff:g id="NUMBER_1">%2$s</xliff:g>-ാം കോളത്തിലെ <xliff:g id="NUMBER_0">%1$s</xliff:g>-ാം വരിയിലേക്ക് നീക്കുക"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ഇഷ്ടമുള്ള <xliff:g id="NUMBER">%1$s</xliff:g> സ്ഥാനത്തേക്ക് നീക്കുക"</string>
<string name="item_moved" msgid="4606538322571412879">"ഇനം നീക്കി"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 0723c25..c71aaff 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Зүйлийг устгалаа"</string>
<string name="undo" msgid="4151576204245173321">"Болих"</string>
<string name="action_move" msgid="4339390619886385032">"Зөөх"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> мөр <xliff:g id="NUMBER_1">%2$s</xliff:g> баганад зөөх"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> дахь <xliff:g id="NUMBER_0">%1$s</xliff:g>-р мөр, <xliff:g id="NUMBER_1">%2$s</xliff:g>-р багана руу зөөх"</string>
<string name="move_to_position" msgid="6750008980455459790">"Байршил <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Дуртай байршил болох <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
<string name="item_moved" msgid="4606538322571412879">"Зөөвөрлөсөн зүйл"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 06c1da7..e871073 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"आयटम काढला"</string>
<string name="undo" msgid="4151576204245173321">"पूर्ववत करा"</string>
<string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये हलवा"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> मधील <xliff:g id="NUMBER_0">%1$s</xliff:g> पंक्ती <xliff:g id="NUMBER_1">%2$s</xliff:g> स्तंभ यावर हलवा"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"आवडत्या <xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
<string name="item_moved" msgid="4606538322571412879">"आयटम हलविला"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d123d14..cbfb551 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
<string name="undo" msgid="4151576204245173321">"Buat asal"</string>
<string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g> dalam <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Alihkan ke kedudukan kegemaran <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Item dialihkan"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index aef6169..984ec4c 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ဖယ်ရှားပြီးပြီ"</string>
<string name="undo" msgid="4151576204245173321">"နောက်ပြန်ရန်"</string>
<string name="action_move" msgid="4339390619886385032">"၎င်းအား ရွှေ့ပါ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> အတိုင် <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့ပါ"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> တွင် အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> ကော်လံ <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့နိုင်သည်"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> သို့ နေရာရွှေ့ပါ"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"စိတ်ကြိုက်နေရာ <xliff:g id="NUMBER">%1$s</xliff:g> သို့ ရွှေ့ပါ"</string>
<string name="item_moved" msgid="4606538322571412879">"၎င်းအားရွှေ့ပြီး"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index eba195c..b29ee77 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
<string name="undo" msgid="4151576204245173321">"Angre"</string>
<string name="action_move" msgid="4339390619886385032">"Flytt elementet"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Flytt til posisjon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytt til posisjonen for favoritter <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Elementet er flyttet"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 4965d78..fcc1ece 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"वस्तु हटाइयो"</string>
<string name="undo" msgid="4151576204245173321">"अन्डू गर्नुहोस्"</string>
<string name="action_move" msgid="4339390619886385032">"वस्तु सार्नुहोस्"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"पङ्क्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तम्भ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा सार्नुहोस्"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"यो वस्तु सारेर <xliff:g id="STRING">%3$s</xliff:g> मा रहेको रो <xliff:g id="NUMBER_0">%1$s</xliff:g> कोलम <xliff:g id="NUMBER_1">%2$s</xliff:g> मा लैजानुहोस्"</string>
<string name="move_to_position" msgid="6750008980455459790">"स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"मन पर्ने स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
<string name="item_moved" msgid="4606538322571412879">"वस्तु सारियो"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index e19b361..bd41952 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string>
<string name="undo" msgid="4151576204245173321">"Ongedaan maken"</string>
<string name="action_move" msgid="4339390619886385032">"Item verplaatsen"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g> voor favorieten"</string>
<string name="item_moved" msgid="4606538322571412879">"Item verplaatst"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 111b9b2..95fa000 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ୍"</string>
<string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍ ଘୁଞ୍ଚାନ୍ତୁ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ଧାଡ଼ି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ରେ ଧାଡି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ସ୍ଥିତିକୁ ନିଅନ୍ତୁ"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ପସନ୍ଦର ସ୍ଥିତି <xliff:g id="NUMBER">%1$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string>
<string name="item_moved" msgid="4606538322571412879">"ଆଇଟମ୍ ଘୁଞ୍ଚେଇ ଦିଆଗଲା"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 3df6886..01fbee0 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ਆਈਟਮ ਹਟਾਈ ਗਈ"</string>
<string name="undo" msgid="4151576204245173321">"ਅਣਕੀਤਾ ਕਰੋ"</string>
<string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ਵਿੱਚ ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਦੇ ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> \'ਤੇ ਜਾਓ"</string>
<string name="move_to_position" msgid="6750008980455459790">"ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ਮਨਪਸੰਦ ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
<string name="item_moved" msgid="4606538322571412879">"ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 869e91b..4b46f0b 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element został usunięty"</string>
<string name="undo" msgid="4151576204245173321">"Cofnij"</string>
<string name="action_move" msgid="4339390619886385032">"Przenieś element"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>, komórka: <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Przenieś do pozycji <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Przenieś do pozycji ulubionych: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Element został przeniesiony"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1512754..dbfa0cb 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removido"</string>
<string name="undo" msgid="4151576204245173321">"Anular"</string>
<string name="action_move" msgid="4339390619886385032">"Mover item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mova para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> em <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string>
<string name="item_moved" msgid="4606538322571412879">"Item movido"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 2dcb98f..3a5ed5b 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Item removido"</string>
<string name="undo" msgid="4151576204245173321">"Desfazer"</string>
<string name="action_move" msgid="4339390619886385032">"Mover item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string>
<string name="item_moved" msgid="4606538322571412879">"Item movido"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9d9fb76..6061ea5 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element eliminat"</string>
<string name="undo" msgid="4151576204245173321">"Anulează"</string>
<string name="action_move" msgid="4339390619886385032">"Mută elementul"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Mută pe rândul <xliff:g id="NUMBER_0">%1$s</xliff:g>, coloana <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mută în rândul <xliff:g id="NUMBER_0">%1$s</xliff:g> coloana <xliff:g id="NUMBER_1">%2$s</xliff:g> din <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Mută pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Mută în preferate, pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Element mutat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9733ac6..0ad12bd 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Объект убран."</string>
<string name="undo" msgid="4151576204245173321">"Отменить"</string>
<string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Переместить в ячейку <xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Переместите в строку <xliff:g id="NUMBER_0">%1$s</xliff:g> столбца <xliff:g id="NUMBER_1">%2$s</xliff:g> на экране \"<xliff:g id="STRING">%3$s</xliff:g>\""</string>
<string name="move_to_position" msgid="6750008980455459790">"Переместить в позицию <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Переместить в Избранное (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string>
<string name="item_moved" msgid="4606538322571412879">"Элемент перемещен."</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index e160e81..cf3dc64 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"අයිතමය ඉවත් කරන ලදි"</string>
<string name="undo" msgid="4151576204245173321">"අස් කරන්න"</string>
<string name="action_move" msgid="4339390619886385032">"අයිතමය ගෙනයන්න"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"පේළිය <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුව <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ගෙන යන්න"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> තුළ <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුවේ<xliff:g id="NUMBER_1">%2$s</xliff:g> පේළියට යන්න"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ස්ථානය වෙත ගෙන යන්න"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ප්රියතම ස්ථානය <xliff:g id="NUMBER">%1$s</xliff:g> වෙත ගෙන යන්න"</string>
<string name="item_moved" msgid="4606538322571412879">"අයිතමය ගෙන යන ලදි"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 8582baf..e3bcc52 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Položka bola odstránená"</string>
<string name="undo" msgid="4151576204245173321">"Späť"</string>
<string name="action_move" msgid="4339390619886385032">"Presunúť položku"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Presunúť do stĺpca <xliff:g id="NUMBER_1">%2$s</xliff:g> v riadku <xliff:g id="NUMBER_0">%1$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premiestnite položku do <xliff:g id="NUMBER_0">%1$s</xliff:g>. riadka <xliff:g id="NUMBER_1">%2$s</xliff:g>. stĺpca na obrazovke <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto v obľúbených položkách"</string>
<string name="item_moved" msgid="4606538322571412879">"Položka bola presunutá"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0762cf7..5f952cf 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element je bil odstranjen."</string>
<string name="undo" msgid="4151576204245173321">"Razveljavi"</string>
<string name="action_move" msgid="4339390619886385032">"Premik elementa"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Premik v <xliff:g id="NUMBER_0">%1$s</xliff:g>. vrstico <xliff:g id="NUMBER_1">%2$s</xliff:g>. stolpca"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premik v vrstico <xliff:g id="NUMBER_0">%1$s</xliff:g> v stolpcu <xliff:g id="NUMBER_1">%2$s</xliff:g> na »<xliff:g id="STRING">%3$s</xliff:g>«"</string>
<string name="move_to_position" msgid="6750008980455459790">"Premk na mesto št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Premik na mesto priljubljenih št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Element je premaknjen"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 821bd50..9f29a87 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
<string name="undo" msgid="4151576204245173321">"Zhbëj"</string>
<string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Kalo te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g> kolona <xliff:g id="NUMBER_1">%2$s</xliff:g> në <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g> i preferencave"</string>
<string name="item_moved" msgid="4606538322571412879">"Artikulli u zhvendos"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 85cc281..948d9b6 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Ставка је уклоњена"</string>
<string name="undo" msgid="4151576204245173321">"Опозови"</string>
<string name="action_move" msgid="4339390619886385032">"Премести ставку"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> и колону <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместите у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колону <xliff:g id="NUMBER_1">%2$s</xliff:g> на <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију у омиљеним"</string>
<string name="item_moved" msgid="4606538322571412879">"Ставка је премештена"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7525eec..c35adb0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Objektet har tagits bort"</string>
<string name="undo" msgid="4151576204245173321">"Ångra"</string>
<string name="action_move" msgid="4339390619886385032">"Flytta objekt"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Flytta till plats <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytta till favoritplats <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Objektet har flyttats"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 3ae5dcf..c233cc4 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Kipengee kimeondolewa"</string>
<string name="undo" msgid="4151576204245173321">"Tendua"</string>
<string name="action_move" msgid="4339390619886385032">"Hamisha kipengee"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g> kwenye <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Hamishia nafasi ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Hamishia nafasi inayopendwa ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Kipengee kimesogezwa"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 0547fc2..bfec460 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string>
<string name="undo" msgid="4151576204245173321">"செயல்தவிர்"</string>
<string name="action_move" msgid="4339390619886385032">"நகர்த்து"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> வரிசை, <xliff:g id="NUMBER_1">%2$s</xliff:g> நெடுவரிசைக்கு நகர்த்து"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> என்பதில் <xliff:g id="NUMBER_0">%1$s</xliff:g>வது வரிசை <xliff:g id="NUMBER_1">%2$s</xliff:g>வது நெடுவரிசைக்கு நகர்த்தப்படும்"</string>
<string name="move_to_position" msgid="6750008980455459790">"நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"விரும்பும் நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
<string name="item_moved" msgid="4606538322571412879">"உருப்படி நகர்த்தப்பட்டது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 5c89925..1d25371 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"ఐటెమ్ తీసివేయబడింది"</string>
<string name="undo" msgid="4151576204245173321">"చర్య రద్దు"</string>
<string name="action_move" msgid="4339390619886385032">"అంశాన్ని తరలించు"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"అడ్డు వరుస <xliff:g id="NUMBER_0">%1$s</xliff:g> నిలువు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>కి తరలించు"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> లో <xliff:g id="NUMBER_0">%1$s</xliff:g> అడ్డు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>నిలువు వరుసకు తరలించండి"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ఇష్టమైనవిలో <xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
<string name="item_moved" msgid="4606538322571412879">"అంశం తరలించబడింది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 07e5796..f633b5f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"นำรายการออกแล้ว"</string>
<string name="undo" msgid="4151576204245173321">"เลิกทำ"</string>
<string name="action_move" msgid="4339390619886385032">"ย้ายรายการ"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"ย้ายไปที่แถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ย้ายไปยังแถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g> ใน <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"ย้ายไปยังตำแหน่ง <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"ย้ายไปยังตำแหน่งที่ <xliff:g id="NUMBER">%1$s</xliff:g> ของรายการโปรด"</string>
<string name="item_moved" msgid="4606538322571412879">"ย้ายรายการแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index f7158b4..de4f952 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Naalis na ang item"</string>
<string name="undo" msgid="4151576204245173321">"I-undo"</string>
<string name="action_move" msgid="4339390619886385032">"Ilipat ang item"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Ilipat sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pumunta sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> sa <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g> sa mga paborito"</string>
<string name="item_moved" msgid="4606538322571412879">"Nalipat ang item"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index b12b9b1..f41284b 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Öğe silindi"</string>
<string name="undo" msgid="4151576204245173321">"Geri al"</string>
<string name="action_move" msgid="4339390619886385032">"Öğeyi taşı"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> sayfasında <xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Favorilerde <xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string>
<string name="item_moved" msgid="4606538322571412879">"Öğe taşındı"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 2b43a4d..360950e 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Елемент вилучено"</string>
<string name="undo" msgid="4151576204245173321">"Відмінити"</string>
<string name="action_move" msgid="4339390619886385032">"Перемістити елемент"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Перемістити в рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перенести: <xliff:g id="STRING">%3$s</xliff:g>, рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Перемістити на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Перемістити у вибране на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string>
<string name="item_moved" msgid="4606538322571412879">"Елемент переміщено"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 70a853f..e68e43e 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"آئٹم ہٹا دیا گیا"</string>
<string name="undo" msgid="4151576204245173321">"کالعدم کریں"</string>
<string name="action_move" msgid="4339390619886385032">"آئٹم منتقل کریں"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> میں قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string>
<string name="move_to_position" msgid="6750008980455459790">"پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"پسندیدہ پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
<string name="item_moved" msgid="4606538322571412879">"آئٹم منتقل کر دیا گیا"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 57a446b..9c7ed56 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Element olib tashlandi"</string>
<string name="undo" msgid="4151576204245173321">"Qaytarish"</string>
<string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g> katakka olish"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ichidagi <xliff:g id="NUMBER_0">%1$s</xliff:g>-qator <xliff:g id="NUMBER_1">%2$s</xliff:g>-ustuniga oʻting"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga olish"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga olish (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string>
<string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 7bbdbd1..7a75ddb 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -56,4 +56,9 @@
<color name="workspace_accent_color_light">@android:color/system_accent1_100</color>
<color name="workspace_accent_color_dark">@android:color/system_accent2_600</color>
+
+ <color name="preload_icon_accent_color_light">@android:color/system_accent1_600</color>
+ <color name="preload_icon_background_color_light">@android:color/system_accent2_200</color>
+ <color name="preload_icon_accent_color_dark">@android:color/system_accent1_300</color>
+ <color name="preload_icon_background_color_dark">@android:color/system_neutral2_700</color>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 2c81ae7..30167fa 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Đã xóa mục"</string>
<string name="undo" msgid="4151576204245173321">"Hủy"</string>
<string name="action_move" msgid="4339390619886385032">"Di chuyển mục"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g> tại <xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Di chuyển tới vị trí <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Di chuyển tới vị trí mục yêu thích <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Đã di chuyển mục"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7c104e2..141d165 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"项目已移除"</string>
<string name="undo" msgid="4151576204245173321">"撤消"</string>
<string name="action_move" msgid="4339390619886385032">"移动项目"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移至<xliff:g id="STRING">%3$s</xliff:g>中的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
<string name="move_to_position" msgid="6750008980455459790">"移至第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏夹第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string>
<string name="item_moved" msgid="4606538322571412879">"已移动项目"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ed288d6..2ff233b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"項目已移除"</string>
<string name="undo" msgid="4151576204245173321">"復原"</string>
<string name="action_move" msgid="4339390619886385032">"移動項目"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"移動至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移去 <xliff:g id="STRING">%3$s</xliff:g> 嘅第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
<string name="move_to_position" msgid="6750008980455459790">"移動至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"移動至喜愛的位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"已移動項目"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3690c3a..91cfd82 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"已移除項目"</string>
<string name="undo" msgid="4151576204245173321">"復原"</string>
<string name="action_move" msgid="4339390619886385032">"移動項目"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移動至 <xliff:g id="STRING">%3$s</xliff:g> 的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
<string name="move_to_position" msgid="6750008980455459790">"移至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"已移動項目"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 26e7dcf..12e8568 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -137,7 +137,7 @@
<string name="item_removed" msgid="851119963877842327">"Into isusiwe"</string>
<string name="undo" msgid="4151576204245173321">"Susa"</string>
<string name="action_move" msgid="4339390619886385032">"Hambisa into"</string>
- <string name="move_to_empty_cell" msgid="2833711483015685619">"Hambisa kurowu engu-<xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu engu-<xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamba emugqeni <xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu <xliff:g id="NUMBER_1">%2$s</xliff:g> ku-<xliff:g id="STRING">%3$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Hambisa kusimo esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="move_to_hotseat_position" msgid="6295412897075147808">"Hambisa kusimo sezintandokazi esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
<string name="item_moved" msgid="4606538322571412879">"Into ihanjisiwe"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 13f20c2..3b71585 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -53,6 +53,8 @@
<attr name="workProfileOverlayTextColor" format="color" />
<attr name="workspaceAccentColor" format="color" />
<attr name="dropTargetHoverTextColor" format="color" />
+ <attr name="preloadIconAccentColor" format="color" />
+ <attr name="preloadIconBackgroundColor" format="color" />
<attr name="allAppsButtonBgColor" format="color" />
<attr name="allAppsButtonColor1" format="color" />
@@ -134,6 +136,10 @@
<attr name="layout_ignoreInsets" format="boolean" />
</declare-styleable>
+ <declare-styleable name="StickyScroller_Layout">
+ <attr name="layout_sticky" format="boolean" />
+ </declare-styleable>
+
<declare-styleable name="GridDisplayOption">
<attr name="name" format="string" />
@@ -170,6 +176,10 @@
<!-- defaults to numColumns, if not specified -->
<attr name="hotseatColumnSpanTwoPanelPortrait" format="integer" />
+ <!-- Spacing to have at the end of the nav buttons in large screen 3 button nav,
+ defaults to @dimen/taskbar_button_margin_default -->
+ <attr name="inlineNavButtonsEndSpacing" format="reference" />
+
<attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" />
<attr name="defaultSplitDisplayLayoutId" format="reference" />
@@ -252,9 +262,10 @@
if not specified -->
<attr name="borderSpaceTwoPanelLandscapeVertical" format="float" />
- <!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
- <!-- defaults to minCellHeight, if not specified -->
+ <!-- defaults to minCellHeight if not specified when GridDisplayOption#isScalable is true.
+ Must be defined when GridDisplayOption#isScalable is false. -->
<attr name="allAppsCellHeight" format="float" />
+ <!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
<!-- defaults to minCellWidth, if not specified -->
<attr name="allAppsCellWidth" format="float" />
<!-- defaults to allAppsCellHeight, if not specified -->
@@ -326,6 +337,24 @@
<!-- defaults to hotseatBorderSpace, if not specified -->
<attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
+ <!-- defaults to res.hotseat_bar_bottom_space_default, if not specified -->
+ <attr name="hotseatBarBottomSpace" format="float" />
+ <!-- defaults to hotseatBarBottomSpace, if not specified -->
+ <attr name="hotseatBarBottomSpaceLandscape" format="float" />
+ <!-- defaults to hotseatBarBottomSpace, if not specified -->
+ <attr name="hotseatBarBottomSpaceTwoPanelLandscape" format="float" />
+ <!-- defaults to hotseatBarBottomSpace, if not specified -->
+ <attr name="hotseatBarBottomSpaceTwoPanelPortrait" format="float" />
+
+ <!-- defaults to res.hotseat_qsb_space_default, if not specified -->
+ <attr name="hotseatQsbSpace" format="float" />
+ <!-- defaults to hotseatQsbSpace, if not specified -->
+ <attr name="hotseatQsbSpaceLandscape" format="float" />
+ <!-- defaults to hotseatQsbSpace, if not specified -->
+ <attr name="hotseatQsbSpaceTwoPanelLandscape" format="float" />
+ <!-- defaults to hotseatQsbSpace, if not specified -->
+ <attr name="hotseatQsbSpaceTwoPanelPortrait" format="float" />
+
<attr name="iconImageSize" format="float" />
<!-- defaults to iconImageSize, if not specified -->
<attr name="iconSizeLandscape" format="float" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 2bc9239..309a1c5 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -85,4 +85,9 @@
<color name="all_apps_button_color_2">#00677E</color>
<color name="all_apps_button_color_3">#5F757E</color>
<color name="all_apps_button_color_4">#005A6E</color>
+
+ <color name="preload_icon_accent_color_light">#00668B</color>
+ <color name="preload_icon_background_color_light">#B5CAD7</color>
+ <color name="preload_icon_accent_color_dark">#4BB6E8</color>
+ <color name="preload_icon_background_color_dark">#40484D</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 9aa1f03..3f94c34 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -130,6 +130,11 @@
<item type="id" name="search_container_all_apps" />
<item type="id" name="search_container_hotseat" />
+ <!-- Scalable Grid configuration -->
+ <!-- This is a float because it is converted to dp later in DeviceProfile -->
+ <dimen name="hotseat_bar_bottom_space_default">48</dimen>
+ <dimen name="hotseat_qsb_space_default">0</dimen>
+
<!-- Recents -->
<item type="id" name="overview_panel"/>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8403af4..b19e739 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,8 +22,6 @@
<dimen name="dynamic_grid_edge_margin">10.77dp</dimen>
<dimen name="dynamic_grid_left_right_margin">8dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
- <!-- Minimum space between workspace and hotseat in spring loaded mode -->
- <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
<!-- Minimum amount of next page visible in spring loaded mode -->
<dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
@@ -32,10 +30,7 @@
<dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
<!-- Hotseat -->
- <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
- <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
<dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
- <dimen name="inline_qsb_bottom_margin">0dp</dimen>
<dimen name="spring_loaded_hotseat_top_margin">76dp</dimen>
<!-- Qsb -->
@@ -44,13 +39,10 @@
it is close to the bottom of the screen -->
<item name="qsb_center_factor" format="float" type="dimen">0.325</item>
- <!-- Extra bottom padding for non-tall devices. -->
- <dimen name="dynamic_grid_hotseat_bottom_non_tall_padding">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen>
<dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
<!-- Scalable Grid -->
- <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen>
+ <dimen name="min_qsb_margin">8dp</dimen>
<!-- Workspace page indicator -->
<dimen name="workspace_page_indicator_height">24dp</dimen>
@@ -364,9 +356,19 @@
<dimen name="taskbar_size">0dp</dimen>
<dimen name="taskbar_stashed_size">0dp</dimen>
<dimen name="qsb_widget_height">0dp</dimen>
+ <dimen name="qsb_shadow_height">0dp</dimen>
<dimen name="taskbar_icon_size">44dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">8dp</dimen>
+ <dimen name="taskbar_nav_buttons_size">0dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">0dp</dimen>
+ <dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
+ <dimen name="taskbar_button_margin_default">0dp</dimen>
+ <dimen name="taskbar_button_space_inbetween">0dp</dimen>
+ <dimen name="taskbar_button_margin_5_5">0dp</dimen>
+ <dimen name="taskbar_button_margin_6_5">0dp</dimen>
+ <dimen name="taskbar_button_margin_4_5">0dp</dimen>
+ <dimen name="taskbar_button_margin_4_4">0dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
@@ -391,8 +393,17 @@
<dimen name="split_placeholder_inset">16dp</dimen>
<dimen name="split_placeholder_icon_size">44dp</dimen>
<dimen name="task_menu_width_grid">216dp</dimen>
-
-
+ <dimen name="split_instructions_radius">22dp</dimen>
+ <dimen name="split_instructions_elevation">1dp</dimen>
+ <dimen name="split_instructions_horizontal_padding">24dp</dimen>
+ <dimen name="split_instructions_vertical_padding">12dp</dimen>
+ <dimen name="split_instructions_bottom_margin_tablet_landscape">32dp</dimen>
+ <dimen name="split_instructions_bottom_margin_tablet_portrait">44dp</dimen>
+ <dimen name="split_instructions_bottom_margin_twopanels_landscape">33dp</dimen>
+ <dimen name="split_instructions_bottom_margin_twopanels_portrait">51dp</dimen>
+ <dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
+ <dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
+
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">28dp</dimen>
<dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
@@ -405,8 +416,12 @@
<!-- Bottom sheet related parameters -->
<dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
+ <dimen name="bottom_sheet_handle_area_height">36dp</dimen>
<dimen name="bottom_sheet_handle_width">32dp</dimen>
<dimen name="bottom_sheet_handle_height">4dp</dimen>
<dimen name="bottom_sheet_handle_margin">16dp</dimen>
<dimen name="bottom_sheet_handle_corner_radius">2dp</dimen>
+
+ <!-- State transition -->
+ <item name="workspace_content_scale" format="float" type="dimen">0.97</item>
</resources>
diff --git a/res/values/id.xml b/res/values/id.xml
index af21b27..9fc0ff8 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -16,7 +16,6 @@
-->
<resources>
<item type="id" name="apps_list_view_work" />
- <item type="id" name="tag_widget_entry" />
<item type="id" name="view_type_widgets_space" />
<item type="id" name="view_type_widgets_list" />
<item type="id" name="view_type_widgets_header" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 847e4a8..2addf50 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -344,7 +344,7 @@
<string name="action_move">Move item</string>
<!-- Accessibility description to move item to empty cell. -->
- <string name="move_to_empty_cell">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g></string>
+ <string name="move_to_empty_cell_description">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g> in <xliff:g id="string" example="Home screen 2 of 4">%3$s</xliff:g></string>
<!-- Accessibility description to move item inside a folder. -->
<string name="move_to_position">Move to position <xliff:g id="number" example="1">%1$s</xliff:g></string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2109510..a8cbf08 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -65,6 +65,8 @@
<item name="workspaceAccentColor">@color/workspace_accent_color_light</item>
<item name="dropTargetHoverTextColor">@color/workspace_text_color_dark</item>
<item name="overviewScrimColor">@color/overview_scrim</item>
+ <item name="preloadIconAccentColor">@color/preload_icon_accent_color_light</item>
+ <item name="preloadIconBackgroundColor">@color/preload_icon_background_color_light</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -123,6 +125,8 @@
<item name="workProfileOverlayTextColor">@android:color/white</item>
<item name="eduHalfSheetBGColor">#DD000000</item>
<item name="overviewScrimColor">@color/overview_scrim_dark</item>
+ <item name="preloadIconAccentColor">@color/preload_icon_accent_color_dark</item>
+ <item name="preloadIconBackgroundColor">@color/preload_icon_background_color_dark</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
@@ -270,7 +274,7 @@
</style>
<!-- Drop targets -->
- <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
+ <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault.Medium">
<item name="android:drawablePadding">@dimen/drop_target_button_drawable_padding</item>
<item name="android:padding">14dp</item>
<item name="android:textColor">@color/drop_target_text</item>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 0802552..0238e7d 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -26,7 +26,7 @@
launcher:numHotseatIcons="3"
launcher:dbFile="launcher_3_by_3.db"
launcher:defaultLayoutId="@xml/default_workspace_3x3"
- launcher:deviceCategory="phone|multi_display" >
+ launcher:deviceCategory="phone" >
<display-option
launcher:name="Super Short Stubby"
@@ -58,6 +58,7 @@
launcher:numFolderColumns="4"
launcher:numHotseatIcons="4"
launcher:dbFile="launcher_4_by_4.db"
+ launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_4_4"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
launcher:deviceCategory="phone|multi_display" >
@@ -121,6 +122,7 @@
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:dbFile="launcher.db"
+ launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_5_5"
launcher:defaultLayoutId="@xml/default_workspace_5x5"
launcher:deviceCategory="phone|multi_display" >
@@ -167,6 +169,7 @@
launcher:hotseatColumnSpanLandscape="4"
launcher:numAllAppsColumns="6"
launcher:isScalable="true"
+ launcher:inlineNavButtonsEndSpacing="@dimen/taskbar_button_margin_6_5"
launcher:devicePaddingId="@xml/paddings_6x5"
launcher:dbFile="launcher_6_by_5.db"
launcher:defaultLayoutId="@xml/default_workspace_6x5"
@@ -199,6 +202,8 @@
launcher:allAppsBorderSpaceLandscape="16"
launcher:hotseatBorderSpace="58"
launcher:hotseatBorderSpaceLandscape="50.4"
+ launcher:hotseatBarBottomSpace="76"
+ launcher:hotseatBarBottomSpaceLandscape="40"
launcher:canBeDefault="true" />
</grid-option>
diff --git a/res/xml/paddings_6x5.xml b/res/xml/paddings_6x5.xml
index a72f554..2f421b7 100644
--- a/res/xml/paddings_6x5.xml
+++ b/res/xml/paddings_6x5.xml
@@ -17,60 +17,29 @@
<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
- <!-- Some non default screen sizes -->
<device-padding
- launcher:maxEmptySpace="30dp">
+ launcher:maxEmptySpace="100dp">
<workspaceTopPadding
- launcher:a="0.34"
+ launcher:a="0.31"
launcher:b="0"/>
<workspaceBottomPadding
- launcher:a="0.26"
+ launcher:a="0.69"
launcher:b="0"/>
<hotseatBottomPadding
- launcher:a="0.4"
- launcher:b="0"/>
- </device-padding>
-
- <device-padding
- launcher:maxEmptySpace="170dp">
- <workspaceTopPadding
launcher:a="0"
- launcher:b="20dp"/>
- <workspaceBottomPadding
- launcher:a="0.4"
- launcher:b="0"
- launcher:c="20dp"/>
- <hotseatBottomPadding
- launcher:a="0.6"
- launcher:b="0"
- launcher:c="20dp"/>
- </device-padding>
-
- <device-padding
- launcher:maxEmptySpace="410dp">
- <workspaceTopPadding
- launcher:a="0"
- launcher:b="112dp"/>
- <workspaceBottomPadding
- launcher:a="0.4"
- launcher:b="0"
- launcher:c="112dp"/>
- <hotseatBottomPadding
- launcher:a="0.6"
- launcher:b="0"
- launcher:c="112dp"/>
+ launcher:b="0"/>
</device-padding>
<device-padding
launcher:maxEmptySpace="9999dp">
<workspaceTopPadding
- launcher:a="0.40"
- launcher:c="36dp"/>
+ launcher:a="0.48"
+ launcher:b="0"/>
<workspaceBottomPadding
- launcher:a="0.60"
- launcher:c="36dp"/>
+ launcher:a="0.52"
+ launcher:b="0"/>
<hotseatBottomPadding
launcher:a="0"
- launcher:b="36dp"/>
+ launcher:b="0"/>
</device-padding>
</device-paddings>
\ No newline at end of file
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index d5b434c..e4aebf6 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -30,6 +30,7 @@
private Handler mHandler;
private OnAlarmListener mAlarmListener;
private boolean mAlarmPending = false;
+ private long mLastSetTimeout;
public Alarm() {
mHandler = new Handler();
@@ -46,6 +47,7 @@
mAlarmPending = true;
long oldTriggerTime = mAlarmTriggerTime;
mAlarmTriggerTime = currentTime + millisecondsInFuture;
+ mLastSetTimeout = millisecondsInFuture;
// If the previous alarm was set for a longer duration, cancel it.
if (mWaitingForCallback && oldTriggerTime > mAlarmTriggerTime) {
@@ -84,4 +86,9 @@
public boolean alarmPending() {
return mAlarmPending;
}
+
+ /** Returns the last value passed to {@link #setAlarm(long)} */
+ public long getLastSetTimeout() {
+ return mLastSetTimeout;
+ }
}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 4b4a017..fbb0a57 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -24,6 +24,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -68,22 +69,6 @@
private final View[] mDragHandles = new View[HANDLE_COUNT];
private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
- private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener =
- new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- // Do nothing
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- // When the app widget view is detached, we should close the resize frame.
- // An example is when the dragging starts, the widget view is detached from
- // CellLayout and then reattached to DragLayout.
- close(false);
- }
- };
-
private LauncherAppWidgetHostView mWidgetView;
private CellLayout mCellLayout;
@@ -221,11 +206,7 @@
private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
- if (mWidgetView != null) {
- mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
- }
mWidgetView = widgetView;
- mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
widgetView.getAppWidgetInfo();
mDragLayer = dragLayer;
@@ -423,6 +404,10 @@
* Based on the current deltas, we determine if and how to resize the widget.
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
+ ViewGroup.LayoutParams wlp = mWidgetView.getLayoutParams();
+ if (!(wlp instanceof CellLayout.LayoutParams)) {
+ return;
+ }
DeviceProfile dp = mLauncher.getDeviceProfile();
float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacePx.x;
float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacePx.y;
@@ -435,7 +420,7 @@
mDirectionVector[0] = 0;
mDirectionVector[1] = 0;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) wlp;
int spanX = lp.cellHSpan;
int spanY = lp.cellVSpan;
@@ -687,9 +672,6 @@
@Override
protected void handleClose(boolean animate) {
mDragLayer.removeView(this);
- if (mWidgetView != null) {
- mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
- }
}
private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 73d3e33..d34f535 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -77,8 +77,8 @@
new ArrayList<>();
protected DeviceProfile mDeviceProfile;
- protected StatsLogManager mStatsLogManager;
protected SystemUiController mSystemUiController;
+ private StatsLogManager mStatsLogManager;
public static final int ACTIVITY_STATE_STARTED = 1 << 0;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 300e7bf..52960a9 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -829,8 +829,8 @@
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- result[0] = (x - hStartPadding) / mCellWidth;
- result[1] = (y - vStartPadding) / mCellHeight;
+ result[0] = (x - hStartPadding) / (mCellWidth + mBorderSpace.x);
+ result[1] = (y - vStartPadding) / (mCellHeight + mBorderSpace.y);
final int xAxis = mCountX;
final int yAxis = mCountY;
@@ -842,16 +842,6 @@
}
/**
- * Given a point, return the cell that most closely encloses that point
- * @param x X coordinate of the point
- * @param y Y coordinate of the point
- * @param result Array of 2 ints to hold the x and y coordinate of the cell
- */
- void pointToCellRounded(int x, int y, int[] result) {
- pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
- }
-
- /**
* Given a cell coordinate, return the point that represents the upper left corner of that cell
*
* @param cellX X coordinate of the cell
@@ -1202,13 +1192,14 @@
int row = cellY + 1;
int col = workspace.mIsRtl ? mCountX - cellX : cellX + 1;
int panelCount = workspace.getPanelCount();
+ int screenId = workspace.getIdForScreen(this);
+ int pageIndex = workspace.getPageIndexForScreenId(screenId);
if (panelCount > 1) {
// Increment the column if the target is on the right side of a two panel home
- int screenId = workspace.getIdForScreen(this);
- int pageIndex = workspace.getPageIndexForScreenId(screenId);
col += (pageIndex % panelCount) * mCountX;
}
- return getContext().getString(R.string.move_to_empty_cell, row, col);
+ return getContext().getString(R.string.move_to_empty_cell_description, row, col,
+ workspace.getPageDescription(pageIndex));
}
}
@@ -1239,7 +1230,7 @@
*/
int[] findNearestVacantArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
int spanY, int[] result, int[] resultSpan) {
- return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, true,
+ return findNearestArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, false,
result, resultSpan);
}
@@ -1261,9 +1252,10 @@
/**
* Find a vacant area that will fit the given bounds nearest the requested
* cell location. Uses Euclidean distance to score multiple vacant areas.
- *
- * @param pixelX The X location at which you want to search for a vacant area.
- * @param pixelY The Y location at which you want to search for a vacant area.
+ * @param relativeXPos The X location relative to the Cell layout at which you want to search
+ * for a vacant area.
+ * @param relativeYPos The Y location relative to the Cell layout at which you want to search
+ * for a vacant area.
* @param minSpanX The minimum horizontal span required
* @param minSpanY The minimum vertical span required
* @param spanX Horizontal span of the object.
@@ -1274,15 +1266,15 @@
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
*/
- private int[] findNearestArea(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX,
- int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) {
+ private int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY,
+ int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) {
lazyInitTempRectStack();
- // For items with a spanX / spanY > 1, the passed in point (pixelX, pixelY) corresponds
- // to the center of the item, but we are searching based on the top-left cell, so
- // we translate the point over to correspond to the top-left.
- pixelX -= mCellWidth * (spanX - 1) / 2f;
- pixelY -= mCellHeight * (spanY - 1) / 2f;
+ // For items with a spanX / spanY > 1, the passed in point (relativeXPos, relativeYPos)
+ // corresponds to the center of the item, but we are searching based on the top-left cell,
+ // so we translate the point over to correspond to the top-left.
+ relativeXPos = (int) (relativeXPos - (mCellWidth + mBorderSpace.x) * (spanX - 1) / 2f);
+ relativeYPos = (int) (relativeYPos - (mCellHeight + mBorderSpace.y) * (spanY - 1) / 2f);
// Keep track of best-scoring drop area
final int[] bestXY = result != null ? result : new int[2];
@@ -1303,7 +1295,7 @@
for (int x = 0; x < countX - (minSpanX - 1); x++) {
int ySize = -1;
int xSize = -1;
- if (ignoreOccupied) {
+ if (!ignoreOccupied) {
// First, let's see if this thing fits anywhere
for (int i = 0; i < minSpanX; i++) {
for (int j = 0; j < minSpanY; j++) {
@@ -1367,7 +1359,7 @@
}
}
validRegions.push(currentRect);
- double distance = Math.hypot(cellXY[0] - pixelX, cellXY[1] - pixelY);
+ double distance = Math.hypot(cellXY[0] - relativeXPos, cellXY[1] - relativeYPos);
if ((distance <= bestDistance && !contained) ||
currentRect.contains(bestRect)) {
@@ -2628,7 +2620,7 @@
* nearest the requested location.
*/
public int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
- return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, false, result, null);
+ return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, true, result, null);
}
boolean existsEmptyCell() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index b276397..3a2227a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -107,15 +107,14 @@
public Rect cellLayoutPaddingPx = new Rect();
public final int edgeMarginPx;
- public float workspaceSpringLoadShrunkTop;
- public float workspaceSpringLoadShrunkBottom;
- public final int workspaceSpringLoadedBottomSpace;
+ public final float workspaceContentScale;
+ private float mWorkspaceSpringLoadShrunkTop;
+ private float mWorkspaceSpringLoadShrunkBottom;
public final int workspaceSpringLoadedMinNextPageVisiblePx;
private final int extraSpace;
public int workspaceTopPadding;
public int workspaceBottomPadding;
- public int extraHotseatBottomPadding;
// Workspace page indicator
public final int workspacePageIndicatorHeight;
@@ -157,24 +156,23 @@
public int folderChildDrawablePaddingPx;
// Hotseat
- public int hotseatBarSizeExtraSpacePx;
public final int numShownHotseatIcons;
public int hotseatCellHeightPx;
- private final int hotseatExtraVerticalSize;
- private final boolean areNavButtonsInline;
+ public final boolean areNavButtonsInline;
// In portrait: size = height, in landscape: size = width
public int hotseatBarSizePx;
- public int hotseatBarTopPaddingPx;
- public final int hotseatBarBottomPaddingPx;
+ public int hotseatBarBottomSpacePx;
+ public int hotseatBarEndOffset;
+ public int hotseatQsbSpace;
public int springLoadedHotseatBarTopMarginPx;
// Start is the side next to the nav bar, end is the side next to the workspace
public final int hotseatBarSidePaddingStartPx;
public final int hotseatBarSidePaddingEndPx;
public final int hotseatQsbHeight;
+ public final int hotseatQsbVisualHeight;
+ private final int hotseatQsbShadowHeight;
public int hotseatBorderSpace;
- public final float qsbBottomMarginOriginalPx;
- public int qsbBottomMarginPx;
public int qsbWidth; // only used when isQsbInline
// All apps
@@ -222,7 +220,7 @@
// Insets
private final Rect mInsets = new Rect();
public final Rect workspacePadding = new Rect();
- private final Rect mHotseatPadding = new Rect();
+ private final Rect mHotseatBarPadding = new Rect();
// When true, nav bar is on the left side of the screen.
private boolean mIsSeascape;
@@ -275,7 +273,7 @@
widthPx = windowBounds.bounds.width();
heightPx = windowBounds.bounds.height();
availableWidthPx = windowBounds.availableSize.x;
- availableHeightPx = windowBounds.availableSize.y;
+ availableHeightPx = windowBounds.availableSize.y;
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -301,6 +299,7 @@
}
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ workspaceContentScale = res.getFloat(R.dimen.workspace_content_scale);
desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
@@ -352,14 +351,15 @@
dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize(
R.dimen.drop_target_button_workspace_edge_gap);
- workspaceSpringLoadedBottomSpace =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize(
R.dimen.dynamic_grid_spring_loaded_min_next_space_visible);
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
+ hotseatQsbShadowHeight = res.getDimensionPixelSize(R.dimen.qsb_shadow_height);
+ hotseatQsbVisualHeight = hotseatQsbHeight - 2 * hotseatQsbShadowHeight;
+
// Whether QSB might be inline in appropriate orientation (e.g. landscape).
boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
@@ -379,17 +379,28 @@
numShownAllAppsColumns =
isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
- hotseatBarSizeExtraSpacePx = 0;
- hotseatBarTopPaddingPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
- if (isQsbInline) {
- hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin);
+
+ int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+ int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin);
+ hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
+ // Have a little space between the inset and the QSB
+ if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
+ int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
+
+ // Only change the spaces if there is space
+ if (availableSpace > 0) {
+ // Make sure there is enough space between hotseat/QSB and QSB/navBar
+ if (availableSpace < minQsbMargin * 2) {
+ minQsbMargin = availableSpace / 2;
+ hotseatQsbSpace = minQsbMargin;
+ } else {
+ hotseatQsbSpace -= minQsbMargin;
+ }
+ }
+ hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
+
} else {
- hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
- : res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ hotseatBarBottomSpacePx = hotseatBarBottomSpace;
}
springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
@@ -398,13 +409,21 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
// Add a bit of space between nav bar and hotseat in vertical bar layout.
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
- hotseatExtraVerticalSize =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
- updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
-
- qsbBottomMarginOriginalPx = isScalableGrid
- ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
- : 0;
+ updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+ if (areNavButtonsInline) {
+ /*
+ * 3 nav buttons +
+ * Spacing between nav buttons +
+ * Little space at the end for contextual buttons +
+ * Little space between icons and nav buttons
+ */
+ hotseatBarEndOffset = 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ + 2 * res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
+ + res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing)
+ + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
+ } else {
+ hotseatBarEndOffset = 0;
+ }
overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
overviewTaskMarginGridPx = res.getDimensionPixelSize(R.dimen.overview_task_margin_grid);
@@ -414,10 +433,7 @@
overviewTaskIconDrawableSizeGridPx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
- // In vertical bar, use the smaller task margin for the top regardless of mode.
- overviewActionsTopMarginPx = isVerticalBarLayout()
- ? overviewTaskMarginPx
- : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
+ overviewActionsTopMarginPx = res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
@@ -446,42 +462,6 @@
workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit);
workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit);
- extraHotseatBottomPadding = Math.round(paddingHotseatBottom * cellScaleToFit);
-
- hotseatBarSizePx += extraHotseatBottomPadding;
-
- qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * cellScaleToFit);
- } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
- // We increase the hotseat size when there is extra space.
-
- if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0
- && extraSpace >= Utilities.dpToPx(TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP)) {
- // For taller devices, we will take a piece of the extra space from each row,
- // and add it to the space above and below the hotseat.
-
- // For devices with more extra space, we take a larger piece from each cell.
- int piece = extraSpace < Utilities.dpToPx(TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP)
- ? 7 : 5;
-
- int extraSpace = ((getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2)
- * inv.numRows) / piece;
-
- workspaceTopPadding = extraSpace / 8;
- int halfLeftOver = (extraSpace - workspaceTopPadding) / 2;
- hotseatBarTopPaddingPx += halfLeftOver;
- hotseatBarSizeExtraSpacePx = halfLeftOver;
- } else {
- // 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.
- hotseatBarSizeExtraSpacePx = getCellSize().y - iconSizePx
- - iconDrawablePaddingPx * 2 - workspacePageIndicatorHeight;
- }
-
- updateHotseatIconSize(iconSizePx);
-
- // Recalculate the available dimensions using the new hotseat size.
- updateAvailableDimensions(res);
}
int cellLayoutPadding =
@@ -537,22 +517,27 @@
: res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
}
- private void updateHotseatIconSize(int hotseatIconSizePx) {
+ /** Updates hotseatCellHeightPx and hotseatBarSizePx */
+ private void updateHotseatSizes(int hotseatIconSizePx) {
// Ensure there is enough space for folder icons, which have a slightly larger radius.
hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR);
+
if (isVerticalBarLayout()) {
hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
+ hotseatBarSidePaddingEndPx;
+ } else if (isQsbInline) {
+ hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight)
+ + hotseatBarBottomSpacePx;
} else {
- hotseatBarSizePx = hotseatIconSizePx + hotseatBarTopPaddingPx
- + hotseatBarBottomPaddingPx + (isScalableGrid ? 0 : hotseatExtraVerticalSize)
- + hotseatBarSizeExtraSpacePx;
+ hotseatBarSizePx = hotseatIconSizePx
+ + hotseatQsbSpace
+ + hotseatQsbVisualHeight
+ + hotseatBarBottomSpacePx;
}
}
private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) {
return getCellLayoutBorderSpace(idp, 1f);
-
}
private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale) {
@@ -764,7 +749,7 @@
// All apps
updateAllAppsIconSize(scale, res);
- updateHotseatIconSize(iconSizePx);
+ updateHotseatSizes(iconSizePx);
// Folder icon
folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
@@ -802,13 +787,13 @@
+ allAppsBorderSpacePx.y;
// but width is just the cell,
// the border is added in #updateAllAppsContainerWidth
- allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
if (isScalableGrid) {
allAppsIconSizePx =
pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics, scale);
allAppsIconTextSizePx =
pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics, scale);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
+ allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
} else {
float invIconSizeDp = inv.allAppsIconSize[mTypeIndex];
float invIconTextSizeSp = inv.allAppsIconTextSize[mTypeIndex];
@@ -816,6 +801,7 @@
allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale);
allAppsIconDrawablePaddingPx =
res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
+ allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
}
updateAllAppsContainerWidth(res);
@@ -932,35 +918,32 @@
* Gets the space in px from the bottom of last item in the vertical-bar hotseat to the
* bottom of the screen.
*/
- public int getVerticalHotseatLastItemBottomOffset() {
+ private int getVerticalHotseatLastItemBottomOffset() {
int cellHeight = calculateCellHeight(
- heightPx - mHotseatPadding.top - mHotseatPadding.bottom, hotseatBorderSpace,
+ heightPx - mHotseatBarPadding.top - mHotseatBarPadding.bottom, hotseatBorderSpace,
numShownHotseatIcons);
- int hotseatSize = (cellHeight * numShownHotseatIcons)
- + (hotseatBorderSpace * (numShownHotseatIcons - 1));
- int extraHotseatEndSpacing = (heightPx - hotseatSize) / 2;
int extraIconEndSpacing = (cellHeight - iconSizePx) / 2;
- return extraHotseatEndSpacing + extraIconEndSpacing + mHotseatPadding.bottom;
+ return extraIconEndSpacing + mHotseatBarPadding.bottom;
}
/**
* Gets the scaled top of the workspace in px for the spring-loaded edit state.
*/
public float getCellLayoutSpringLoadShrunkTop() {
- workspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+ mWorkspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+ dropTargetBarBottomMarginPx;
- return workspaceSpringLoadShrunkTop;
+ return mWorkspaceSpringLoadShrunkTop;
}
/**
* Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
*/
- private float getCellLayoutSpringLoadShrunkBottom() {
+ public float getCellLayoutSpringLoadShrunkBottom() {
int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
- workspaceSpringLoadShrunkBottom =
+ mWorkspaceSpringLoadShrunkBottom =
heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset()
: topOfHotseat);
- return workspaceSpringLoadShrunkBottom;
+ return mWorkspaceSpringLoadShrunkBottom;
}
/**
@@ -1022,10 +1005,11 @@
padding.right = hotseatBarSizePx;
}
} else {
- // Pad the bottom of the workspace with search/hotseat bar sizes
- int hotseatTop = hotseatBarSizePx;
- int paddingBottom = hotseatTop + workspacePageIndicatorHeight
- + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
+ // Pad the bottom of the workspace with hotseat bar
+ // and leave a bit of space in case a widget go all the way down
+ int paddingBottom = hotseatBarSizePx + workspaceBottomPadding
+ + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace
+ - mInsets.bottom;
int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx);
int paddingSide = desiredWorkspaceHorizontalMarginPx;
@@ -1065,50 +1049,49 @@
+ diffOverlapFactor), 0);
if (isSeascape()) {
- mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
+ mHotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
hotseatBarSidePaddingEndPx, paddingBottom);
} else {
- mHotseatPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
+ mHotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
}
} else if (isTaskbarPresent) {
// Center the QSB vertically with hotseat
- int hotseatBottomPadding = getHotseatBottomPadding();
- int hotseatTopPadding =
- workspacePadding.bottom - hotseatBottomPadding - hotseatCellHeightPx;
+ int hotseatBarBottomPadding = getHotseatBarBottomPadding();
+ int hotseatBarTopPadding =
+ hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
// Push icons to the side
int additionalQsbSpace = isQsbInline ? qsbWidth + hotseatBorderSpace : 0;
int requiredWidth = iconSizePx * numShownHotseatIcons
+ hotseatBorderSpace * (numShownHotseatIcons - 1)
+ additionalQsbSpace;
- int endOffset = ApiWrapper.getHotseatEndOffset(context);
- int hotseatWidth = Math.min(requiredWidth, availableWidthPx - endOffset);
+ int hotseatWidth = Math.min(requiredWidth, availableWidthPx - hotseatBarEndOffset);
int sideSpacing = (availableWidthPx - hotseatWidth) / 2;
- mHotseatPadding.set(sideSpacing, hotseatTopPadding, sideSpacing, hotseatBottomPadding);
+ mHotseatBarPadding.set(sideSpacing, hotseatBarTopPadding, sideSpacing,
+ hotseatBarBottomPadding);
boolean isRtl = Utilities.isRtl(context.getResources());
if (isRtl) {
- mHotseatPadding.right += additionalQsbSpace;
+ mHotseatBarPadding.right += additionalQsbSpace;
} else {
- mHotseatPadding.left += additionalQsbSpace;
+ mHotseatBarPadding.left += additionalQsbSpace;
}
- if (endOffset > sideSpacing) {
+ if (hotseatBarEndOffset > sideSpacing) {
int diff = isRtl
- ? sideSpacing - endOffset
- : endOffset - sideSpacing;
- mHotseatPadding.left -= diff;
- mHotseatPadding.right += diff;
+ ? sideSpacing - hotseatBarEndOffset
+ : hotseatBarEndOffset - sideSpacing;
+ mHotseatBarPadding.left -= diff;
+ mHotseatBarPadding.right += diff;
}
} else if (isScalableGrid) {
int sideSpacing = (availableWidthPx - qsbWidth) / 2;
- mHotseatPadding.set(sideSpacing,
- hotseatBarTopPaddingPx,
+ mHotseatBarPadding.set(sideSpacing,
+ 0,
sideSpacing,
- hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
- + mInsets.bottom);
+ getHotseatBarBottomPadding());
} else {
// We want the edges of the hotseat to line up with the edges of the workspace, but the
// icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -1117,14 +1100,15 @@
float workspaceCellWidth = (float) widthPx / inv.numColumns;
float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
- mHotseatPadding.set(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
- + mInsets.left, hotseatBarTopPaddingPx,
+ mHotseatBarPadding.set(
+ hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
+ + mInsets.left,
+ 0,
hotseatAdjustment + workspacePadding.right + cellLayoutPaddingPx.right
+ mInsets.right,
- hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
- + mInsets.bottom);
+ getHotseatBarBottomPadding());
}
- return mHotseatPadding;
+ return mHotseatBarPadding;
}
/**
@@ -1132,27 +1116,22 @@
*/
public int getQsbOffsetY() {
if (isQsbInline) {
- return hotseatBarBottomPaddingPx;
- }
-
- int freeSpace = isTaskbarPresent
- ? workspacePadding.bottom
- : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
-
- if (isScalableGrid && qsbBottomMarginPx > mInsets.bottom) {
- // Note that taskbarSize = 0 unless isTaskbarPresent.
- return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace);
+ return getHotseatBarBottomPadding() - ((hotseatQsbHeight - hotseatCellHeightPx) / 2);
+ } else if (isTaskbarPresent) { // QSB on top
+ return hotseatBarSizePx - hotseatQsbHeight + hotseatQsbShadowHeight;
} else {
- return (int) (freeSpace * mQsbCenterFactor)
- + (isTaskbarPresent ? taskbarSize : mInsets.bottom);
+ return hotseatBarBottomSpacePx - hotseatQsbShadowHeight;
}
}
- private int getHotseatBottomPadding() {
- if (isQsbInline) {
- return getQsbOffsetY() - (Math.abs(hotseatQsbHeight - hotseatCellHeightPx) / 2);
+ /**
+ * Returns the number of pixels the hotseat is translated from the bottom of the screen.
+ */
+ private int getHotseatBarBottomPadding() {
+ if (isTaskbarPresent) { // QSB on top or inline
+ return hotseatBarBottomSpacePx - (Math.abs(hotseatCellHeightPx - iconSizePx) / 2);
} else {
- return (getQsbOffsetY() - taskbarSize) / 2;
+ return hotseatBarSizePx - hotseatCellHeightPx;
}
}
@@ -1163,7 +1142,7 @@
int taskbarIconBottomSpace = (taskbarSize - iconSizePx) / 2;
int launcherIconBottomSpace =
Math.min((hotseatCellHeightPx - iconSizePx) / 2, gridVisualizationPaddingY);
- return getHotseatBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
+ return getHotseatBarBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
}
/**
@@ -1172,7 +1151,7 @@
public int getOverviewActionsClaimedSpaceBelow() {
if (isTaskbarPresent && !isGestureMode) {
// Align vertically to where nav buttons are.
- return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
+ return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
}
return isTaskbarPresent ? stashedTaskbarSize : mInsets.bottom;
@@ -1267,6 +1246,7 @@
return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
}
+ // LINT.IfChange
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "DeviceProfile:");
writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
@@ -1346,7 +1326,8 @@
allAppsIconDrawablePaddingPx));
writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx));
- writer.println(prefix + pxToDpStr("allAppsBorderSpacePx", allAppsBorderSpacePx.x));
+ writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", allAppsBorderSpacePx.x));
+ writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", allAppsBorderSpacePx.y));
writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding));
writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
@@ -1354,18 +1335,20 @@
writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
- writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
- writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
+ writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx));
writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
hotseatBarSidePaddingStartPx));
writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
hotseatBarSidePaddingEndPx));
+ writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset));
+ writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace));
+ writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
writer.println(prefix + pxToDpStr("springLoadedHotseatBarTopMarginPx",
springLoadedHotseatBarTopMarginPx));
- writer.println(prefix + pxToDpStr("mHotseatPadding.top", mHotseatPadding.top));
- writer.println(prefix + pxToDpStr("mHotseatPadding.bottom", mHotseatPadding.bottom));
- writer.println(prefix + pxToDpStr("mHotseatPadding.left", mHotseatPadding.left));
- writer.println(prefix + pxToDpStr("mHotseatPadding.right", mHotseatPadding.right));
+ writer.println(prefix + pxToDpStr("mHotseatBarPadding.top", mHotseatBarPadding.top));
+ writer.println(prefix + pxToDpStr("mHotseatBarPadding.bottom", mHotseatBarPadding.bottom));
+ writer.println(prefix + pxToDpStr("mHotseatBarPadding.left", mHotseatBarPadding.left));
+ writer.println(prefix + pxToDpStr("mHotseatBarPadding.right", mHotseatBarPadding.right));
writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
writer.println(prefix + pxToDpStr("hotseatBorderSpace", hotseatBorderSpace));
writer.println(prefix + "\tisQsbInline: " + isQsbInline);
@@ -1394,7 +1377,6 @@
}
writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
- writer.println(prefix + pxToDpStr("extraHotseatBottomPadding", extraHotseatBottomPadding));
writer.println(prefix + pxToDpStr("overviewTaskMarginPx", overviewTaskMarginPx));
writer.println(prefix + pxToDpStr("overviewTaskMarginGridPx", overviewTaskMarginGridPx));
@@ -1421,16 +1403,29 @@
prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
writer.println(
- prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
+ prefix + pxToDpStr("workspaceSpringLoadShrunkTop", mWorkspaceSpringLoadShrunkTop));
writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
- workspaceSpringLoadShrunkBottom));
- writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace",
- workspaceSpringLoadedBottomSpace));
+ mWorkspaceSpringLoadShrunkBottom));
writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
workspaceSpringLoadedMinNextPageVisiblePx));
writer.println(
prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
+ writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight()));
+ writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth()));
}
+ // LINT.ThenChange(
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt,
+ // packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt)
private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
Configuration config = new Configuration(c.getResources().getConfiguration());
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index d908440..c1304d4 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -194,9 +194,10 @@
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
firstButton.measure(widthSpec, heightSpec);
if (!mIsVertical) {
- // Remove icons and put the button's text on two lines if text is truncated.
+ // Remove both icons and put the button's text on two lines if text is truncated.
if (firstButton.isTextTruncated(availableWidth)) {
firstButton.setIconVisible(false);
+ secondButton.setIconVisible(false);
firstButton.setTextMultiLine(true);
firstButton.setPadding(horizontalPadding, verticalPadding / 2,
horizontalPadding, verticalPadding / 2);
@@ -209,8 +210,10 @@
}
secondButton.measure(widthSpec, heightSpec);
if (!mIsVertical) {
+ // Remove both icons and put the button's text on two lines if text is truncated.
if (secondButton.isTextTruncated(availableWidth)) {
secondButton.setIconVisible(false);
+ firstButton.setIconVisible(false);
secondButton.setTextMultiLine(true);
secondButton.setPadding(horizontalPadding, verticalPadding / 2,
horizontalPadding, verticalPadding / 2);
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index f117069..94903f2 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,6 +24,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -86,15 +87,20 @@
* Returns the available scroll height:
* AvailableScrollHeight = Total height of the all items - last page height
*/
- protected abstract int getAvailableScrollHeight();
+ protected int getAvailableScrollHeight() {
+ // AvailableScrollHeight = Total height of the all items - first page height
+ int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+ int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
+ int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+ return Math.max(0, availableScrollHeight);
+ }
/**
* Returns the available scroll bar height:
* AvailableScrollBarHeight = Total height of the visible view - thumb height
*/
protected int getAvailableScrollBarHeight() {
- int availableScrollBarHeight = getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
- return availableScrollBarHeight;
+ return getScrollbarTrackHeight() - mScrollbar.getThumbHeight();
}
/**
@@ -152,12 +158,51 @@
}
/**
- * Maps the touch (from 0..1) to the adapter position that should be visible.
- * <p>Override in each subclass of this base class.
- *
* @return the scroll top of this recycler view.
*/
- public abstract int getCurrentScrollY();
+ public int getCurrentScrollY() {
+ Adapter adapter = getAdapter();
+ if (adapter == null) {
+ return -1;
+ }
+ if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+ return -1;
+ }
+
+ int itemPosition = NO_POSITION;
+ View child = null;
+
+ LayoutManager layoutManager = getLayoutManager();
+ if (layoutManager instanceof LinearLayoutManager) {
+ // Use the LayoutManager as the source of truth for visible positions. During
+ // animations, the view group child may not correspond to the visible views that appear
+ // at the top.
+ itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
+ child = layoutManager.findViewByPosition(itemPosition);
+ }
+
+ if (child == null) {
+ // If the layout manager returns null for any reason, which can happen before layout
+ // has occurred for the position, then look at the child of this view as a ViewGroup.
+ child = getChildAt(0);
+ itemPosition = getChildAdapterPosition(child);
+ }
+ if (itemPosition == NO_POSITION) {
+ return -1;
+ }
+ return getPaddingTop() + getItemsHeight(itemPosition)
+ - layoutManager.getDecoratedTop(child);
+ }
+
+ /**
+ * Returns the sum of the height, in pixels, of this list adapter's items from index
+ * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+ * it returns the full height of all the items.
+ *
+ * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ protected abstract int getItemsHeight(int untilIndex);
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 76106fc..05ed319 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -111,9 +111,7 @@
mQsb.setVisibility(View.VISIBLE);
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = grid.isTaskbarPresent
- ? grid.workspacePadding.bottom
- : grid.hotseatBarSizePx + insets.bottom;
+ lp.height = grid.hotseatBarSizePx;
}
Rect padding = grid.getHotseatLayoutPadding(getContext());
@@ -206,6 +204,13 @@
getShortcutsAndWidgets().setAlpha(alpha);
}
+ /**
+ * Sets the alpha value of just our QSB.
+ */
+ public void setQsbAlpha(float alpha) {
+ mQsb.setAlpha(alpha);
+ }
+
public float getIconsAlpha() {
return getShortcutsAndWidgets().getAlpha();
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index db43b44..c051292 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -46,6 +46,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.core.content.res.ResourcesCompat;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.provider.RestoreDbTask;
@@ -61,8 +62,6 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -79,7 +78,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
- public @interface DeviceType{}
+ public @interface DeviceType {}
+
public static final int TYPE_PHONE = 0;
public static final int TYPE_MULTI_DISPLAY = 1;
public static final int TYPE_TABLET = 2;
@@ -127,6 +127,7 @@
public PointF[] borderSpaces;
public float folderBorderSpace;
public float[] hotseatBorderSpaces;
+ public int inlineNavButtonsEndSpacing;
public float[] horizontalMargin;
@@ -155,6 +156,8 @@
public int numDatabaseHotseatIcons;
public int[] hotseatColumnSpan;
+ public float[] hotseatBarBottomSpace;
+ public float[] hotseatQsbSpace;
/**
* Number of columns in the all apps list.
@@ -234,7 +237,8 @@
/*allowDisabledGrid=*/false),
defaultDeviceType);
- Info myInfo = new Info(context, display);
+ Context displayContext = context.createDisplayContext(display);
+ Info myInfo = new Info(displayContext);
@DeviceType int deviceType = getDeviceType(myInfo);
DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo,
@@ -336,6 +340,8 @@
devicePaddingId = closestProfile.devicePaddingId;
this.deviceType = deviceType;
+ inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
+
mExtraAttrs = closestProfile.extraAttrs;
iconSize = displayOption.iconSizes;
@@ -361,6 +367,8 @@
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
hotseatColumnSpan = closestProfile.hotseatColumnSpan;
hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
+ hotseatBarBottomSpace = displayOption.hotseatBarBottomSpace;
+ hotseatQsbSpace = displayOption.hotseatQsbSpace;
numAllAppsColumns = closestProfile.numAllAppsColumns;
numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
@@ -633,18 +641,6 @@
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context);
- if (Utilities.IS_DEBUG_DEVICE) {
- StringWriter stringWriter = new StringWriter();
- PrintWriter printWriter = new PrintWriter(stringWriter);
- DisplayController.INSTANCE.get(context).dump(printWriter);
- printWriter.flush();
- Log.d("b/231312158", "getDeviceProfile -"
- + "\nconfig: " + config
- + "\ndisplayMetrics: " + res.getDisplayMetrics()
- + "\nrotation: " + rotation
- + "\n" + stringWriter.toString(),
- new Exception());
- }
return getBestMatch(screenWidth, screenHeight, rotation);
}
@@ -739,8 +735,10 @@
private final int numHotseatIcons;
private final int numShrunkenHotseatIcons;
private final int numDatabaseHotseatIcons;
+
private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
+ private int inlineNavButtonsEndSpacing;
private final String dbFile;
private final int defaultLayoutId;
@@ -779,6 +777,7 @@
R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
numDatabaseHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+
hotseatColumnSpan[INDEX_DEFAULT] = a.getInt(
R.styleable.GridDisplayOption_hotseatColumnSpan, numColumns);
hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt(
@@ -790,6 +789,9 @@
R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelPortrait,
numColumns);
+ inlineNavButtonsEndSpacing =
+ a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing,
+ R.dimen.taskbar_button_margin_default);
numFolderRows = a.getInt(
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
@@ -838,6 +840,8 @@
private final float[] horizontalMargin = new float[COUNT_SIZES];
//TODO(http://b/228998082) remove this when 3 button spaces are fixed
private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
+ private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
+ private final float[] hotseatQsbSpace = new float[COUNT_SIZES];
private final float[] iconSizes = new float[COUNT_SIZES];
private final float[] textSizes = new float[COUNT_SIZES];
@@ -1063,6 +1067,34 @@
R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
hotseatBorderSpaces[INDEX_DEFAULT]);
+ hotseatBarBottomSpace[INDEX_DEFAULT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBarBottomSpace,
+ ResourcesCompat.getFloat(context.getResources(),
+ R.dimen.hotseat_bar_bottom_space_default));
+ hotseatBarBottomSpace[INDEX_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceLandscape,
+ hotseatBarBottomSpace[INDEX_DEFAULT]);
+ hotseatBarBottomSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelLandscape,
+ hotseatBarBottomSpace[INDEX_DEFAULT]);
+ hotseatBarBottomSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelPortrait,
+ hotseatBarBottomSpace[INDEX_DEFAULT]);
+
+ hotseatQsbSpace[INDEX_DEFAULT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatQsbSpace,
+ ResourcesCompat.getFloat(context.getResources(),
+ R.dimen.hotseat_qsb_space_default));
+ hotseatQsbSpace[INDEX_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatQsbSpaceLandscape,
+ hotseatQsbSpace[INDEX_DEFAULT]);
+ hotseatQsbSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelLandscape,
+ hotseatQsbSpace[INDEX_DEFAULT]);
+ hotseatQsbSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelPortrait,
+ hotseatQsbSpace[INDEX_DEFAULT]);
+
a.recycle();
}
@@ -1098,6 +1130,8 @@
minCellSize[i].y *= w;
horizontalMargin[i] *= w;
hotseatBorderSpaces[i] *= w;
+ hotseatBarBottomSpace[i] *= w;
+ hotseatQsbSpace[i] *= w;
allAppsCellSize[i].x *= w;
allAppsCellSize[i].y *= w;
allAppsIconSizes[i] *= w;
@@ -1121,6 +1155,8 @@
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+ hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i];
+ hotseatQsbSpace[i] += p.hotseatQsbSpace[i];
allAppsCellSize[i].x += p.allAppsCellSize[i].x;
allAppsCellSize[i].y += p.allAppsCellSize[i].y;
allAppsIconSizes[i] += p.allAppsIconSizes[i];
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ebed31b..9c62251 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -30,10 +30,12 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
+import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -97,6 +99,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.util.FloatProperty;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -195,6 +198,7 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.ViewCapture;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.FloatingIconView;
@@ -266,7 +270,7 @@
protected static final int REQUEST_LAST = 100;
// Type: int
- private static final String RUNTIME_STATE = "launcher.state";
+ protected static final String RUNTIME_STATE = "launcher.state";
// Type: PendingRequestArgs
private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
// Type: int
@@ -278,6 +282,9 @@
// Type int[]
private static final String RUNTIME_STATE_CURRENT_SCREEN_IDS = "launcher.current_screen_ids";
+ // Type PendingSplitSelectInfo<Parcelable>
+ protected static final String PENDING_SPLIT_SELECT_INFO = "launcher.pending_split_select_info";
+
public static final String ON_CREATE_EVT = "Launcher.onCreate";
public static final String ON_START_EVT = "Launcher.onStart";
public static final String ON_RESUME_EVT = "Launcher.onResume";
@@ -299,13 +306,17 @@
public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;
+ private static final FloatProperty<Workspace<?>> WORKSPACE_WIDGET_SCALE =
+ WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
+ private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
+ HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
+
private Configuration mOldConfig;
@Thunk
Workspace<?> mWorkspace;
@Thunk
DragLayer mDragLayer;
- private DragController mDragController;
private WidgetManagerHelper mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
@@ -369,6 +380,7 @@
private RotationHelper mRotationHelper;
protected LauncherOverlayManager mOverlayManager;
+ protected DragController mDragController;
// If true, overlay callbacks are deferred
private boolean mDeferOverlayCallbacks;
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
@@ -386,6 +398,7 @@
private LauncherState mPrevLauncherState;
private StringCache mStringCache;
+ private ViewCapture mViewCapture;
@Override
@TargetApi(Build.VERSION_CODES.S)
@@ -465,7 +478,7 @@
mIconCache = app.getIconCache();
mAccessibilityDelegate = createAccessibilityDelegate();
- mDragController = new LauncherDragController(this);
+ initDragController();
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
@@ -614,6 +627,13 @@
super.onConfigurationChanged(newConfig);
}
+ /**
+ * Initializes the drag controller.
+ */
+ protected void initDragController() {
+ mDragController = new LauncherDragController(this);
+ }
+
@Override
public void onIdpChanged(boolean modelPropertiesChanged) {
initDeviceProfile(mDeviceProfile.inv);
@@ -740,7 +760,7 @@
completeAddAppWidget(appWidgetId, info, null, null);
break;
case REQUEST_RECONFIGURE_APPWIDGET:
- mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
+ getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
break;
case REQUEST_BIND_PENDING_APPWIDGET: {
@@ -1080,10 +1100,6 @@
}
addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
- if (state.hasFlag(FLAG_CLOSE_POPUPS)) {
- AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE));
- }
-
if (state == SPRING_LOADED) {
// Prevent any Un/InstallShortcutReceivers from updating the db while we are
// not on homescreen
@@ -1473,6 +1489,14 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
mOverlayManager.onAttachedToWindow();
+ if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+ View root = getDragLayer().getRootView();
+ if (mViewCapture != null) {
+ root.getViewTreeObserver().removeOnDrawListener(mViewCapture);
+ }
+ mViewCapture = new ViewCapture(root);
+ root.getViewTreeObserver().addOnDrawListener(mViewCapture);
+ }
}
@Override
@@ -2992,6 +3016,10 @@
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
+ if (mViewCapture != null) {
+ writer.println(prefix + "\tmViewCapture: " + mViewCapture.dumpToString());
+ }
+
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
@@ -3220,7 +3248,12 @@
* @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets.
*/
public void onWidgetsTransition(float progress) {
- // No-Op
+ if (mDeviceProfile.isTablet) {
+ float scale =
+ Utilities.comp(Utilities.comp(mDeviceProfile.workspaceContentScale) * progress);
+ WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
+ HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
+ }
}
private static class NonConfigInstance {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 808bf96..b858d1a 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -26,6 +26,7 @@
import android.util.IntProperty;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.widget.TextView;
import com.android.launcher3.util.MultiScalePropertyFactory;
@@ -80,9 +81,9 @@
new MultiScalePropertyFactory<Hotseat>("hotseat_scale_property");
public static final int SCALE_INDEX_UNFOLD_ANIMATION = 1;
- public static final int SCALE_INDEX_UNLOCK_ANIMATION = 2;
- public static final int SCALE_INDEX_WORKSPACE_STATE = 3;
- public static final int SCALE_INDEX_REVEAL_ANIM = 4;
+ public static final int SCALE_INDEX_WORKSPACE_STATE = 2;
+ public static final int SCALE_INDEX_REVEAL_ANIM = 3;
+ public static final int SCALE_INDEX_WIDGET_TRANSITION = 4;
/** Increase the duration if we prevented the fling, as we are going against a high velocity. */
public static int blockedFlingDurationFactor(float velocity) {
@@ -115,6 +116,32 @@
}
};
+ public static final IntProperty<TextView> TEXT_COLOR =
+ new IntProperty<TextView>("textColor") {
+ @Override
+ public Integer get(TextView view) {
+ return view.getTextColors().getDefaultColor();
+ }
+
+ @Override
+ public void setValue(TextView view, int color) {
+ view.setTextColor(color);
+ }
+ };
+
+ public static final IntProperty<TextView> HINT_TEXT_COLOR =
+ new IntProperty<TextView>("hintTextColor") {
+ @Override
+ public Integer get(TextView view) {
+ return view.getHintTextColors().getDefaultColor();
+ }
+
+ @Override
+ public void setValue(TextView view, int color) {
+ view.setHintTextColor(color);
+ }
+ };
+
public static final FloatProperty<View> VIEW_TRANSLATE_X =
View.TRANSLATION_X instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_X
: new FloatProperty<View>("translateX") {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5aa8a46..a20ff8c 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -162,7 +162,7 @@
private synchronized boolean prepForMigration(String dbFile, String targetTableName,
Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
- Log.e("b/198965093", "prepForMigration - target db is same as current: " + dbFile);
+ Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
return false;
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index ea6a919..8b5cdbd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -208,14 +208,18 @@
return (getVisibleElements(launcher) & elements) == elements;
}
- /** Returns whether taskbar is stashed and thus should replace hotseat with a handle */
+ /**
+ * Returns whether taskbar is stashed and thus should either:
+ * 1) replace hotseat or taskbar icons with a handle in gesture navigation mode or
+ * 2) fade out the hotseat or taskbar icons in 3-button navigation mode.
+ */
public boolean isTaskbarStashed(Launcher launcher) {
return false;
}
/** Returns whether taskbar is aligned with the hotseat vs position inside apps */
public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
- return !isTaskbarStashed(launcher);
+ return true;
}
/**
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index cba0b7d..73be5be 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1187,9 +1187,7 @@
}
public int getScrollForPage(int index) {
- // TODO(b/233112195): Use !pageScrollsInitialized() instead of mPageScrolls == null, once we
- // root cause where we should be using runOnPageScrollsInitialized().
- if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) {
+ if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
return 0;
} else {
return mPageScrolls[index];
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7b96838..dc8c739 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -587,15 +587,6 @@
}
/**
- * Returns an intent for starting the default home activity
- */
- public static Intent createHomeIntent() {
- return new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- /**
* Wraps a message with a TTS span, so that a different message is spoken than
* what is getting displayed.
* @param msg original message
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4903d77..e6dea8f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -159,7 +159,7 @@
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
- private ShortcutAndWidgetContainer mDragSourceInternal;
+ protected ShortcutAndWidgetContainer mDragSourceInternal;
@Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
@Thunk final IntArray mScreenOrder = new IntArray();
@@ -195,7 +195,7 @@
@Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
- private final int[] mTempXY = new int[2];
+ protected final int[] mTempXY = new int[2];
private final float[] mTempFXY = new float[2];
private final Rect mTempRect = new Rect();
@Thunk float[] mDragViewVisualCenter = new float[2];
@@ -3418,7 +3418,11 @@
return getPageDescription(page);
}
- private String getPageDescription(int page) {
+ /**
+ * @param page page index.
+ * @return Description of the page at the given page index.
+ */
+ public String getPageDescription(int page) {
int nScreens = getChildCount();
int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (extraScreenId >= 0 && nScreens > 1) {
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index af17cf7..de34416 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -31,7 +31,6 @@
import android.util.Log;
import android.util.SparseIntArray;
import android.view.MotionEvent;
-import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
@@ -342,24 +341,7 @@
}
@Override
- public int getCurrentScrollY() {
- // Return early if there are no items or we haven't been measured
- List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
- if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
- return -1;
- }
-
- // Calculate the y and offset for the item
- View child = getChildAt(0);
- int position = getChildAdapterPosition(child);
- if (position == NO_POSITION) {
- return -1;
- }
- return getPaddingTop() +
- getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
- }
-
- public int getCurrentScrollY(int position, int offset) {
+ protected int getItemsHeight(int position) {
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
AllAppsGridAdapter.AdapterItem posItem = position < items.size()
? items.get(position) : null;
@@ -400,17 +382,7 @@
}
mCachedScrollPositions.put(position, y);
}
- return y - offset;
- }
-
- /**
- * Returns the available scroll height:
- * AvailableScrollHeight = Total height of the all items - last page height
- */
- @Override
- protected int getAvailableScrollHeight() {
- return getPaddingTop() + getCurrentScrollY(getAdapter().getItemCount(), 0)
- - getHeight() + getPaddingBottom();
+ return y;
}
public int getScrollBarTop() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a4a2085..bdab03f 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -23,7 +23,6 @@
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_VERTICAL_PROGRESS;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -37,7 +36,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
@@ -46,7 +44,6 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.MultiAdditivePropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.views.ScrimView;
/**
@@ -231,11 +228,11 @@
public void setStateWithAnimation(LauncherState toState,
StateAnimationConfig config, PendingAnimation builder) {
if (NORMAL.equals(toState) && mLauncher.isInState(ALL_APPS)) {
- UiThreadHelper.hideKeyboardAsync(mLauncher, mLauncher.getAppsView().getWindowToken());
builder.addEndListener(success -> {
// Reset pull back progress and alpha after switching states.
ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
ALL_APPS_PULL_BACK_ALPHA.set(this, 1f);
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
});
}
@@ -243,7 +240,6 @@
if (Float.compare(mProgress, targetProgress) == 0) {
setAlphas(toState, config, builder);
// Fail fast
- onProgressAnimationEnd();
return;
}
@@ -293,11 +289,6 @@
public void setupViews(ScrimView scrimView, ActivityAllAppsContainerView<Launcher> appsView) {
mScrimView = scrimView;
mAppsView = appsView;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) {
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- }
mAppsView.setScrimView(scrimView);
mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT);
mAppsViewAlpha.setUpdateVisibility(true);
@@ -318,6 +309,7 @@
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
}
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 45a567d..8bbe216 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -50,7 +50,7 @@
public static final String TAG = "AlphabeticalAppsList";
- private final WorkAdapterProvider mWorkAdapterProvider;
+ private final WorkProfileManager mWorkProviderManager;
/**
* Info about a fast scroller section, depending if sections are merged, the fast scroller
@@ -92,11 +92,11 @@
private Predicate<ItemInfo> mItemFilter;
public AlphabeticalAppsList(Context context, @Nullable AllAppsStore appsStore,
- WorkAdapterProvider adapterProvider) {
+ WorkProfileManager workProfileManager) {
mAllAppsStore = appsStore;
mActivityContext = ActivityContext.lookupContext(context);
mAppNameComparator = new AppInfoComparator(context);
- mWorkAdapterProvider = adapterProvider;
+ mWorkProviderManager = workProfileManager;
mNumAppsPerRowAllApps = mActivityContext.getDeviceProfile().inv.numAllAppsColumns;
if (mAllAppsStore != null) {
mAllAppsStore.addUpdateListener(this);
@@ -260,9 +260,9 @@
}
} else {
int position = 0;
- if (mWorkAdapterProvider != null) {
- position += mWorkAdapterProvider.addWorkItems(mAdapterItems);
- if (!mWorkAdapterProvider.shouldShowWorkApps()) {
+ if (mWorkProviderManager != null) {
+ position += mWorkProviderManager.addWorkItems(mAdapterItems);
+ if (!mWorkProviderManager.shouldShowWorkApps()) {
return;
}
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index fcba246..459fa88 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -63,6 +63,11 @@
// A divider that separates the apps list and the search market button
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
+ public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 5;
+ public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 6;
+
+ public static final int NEXT_ID = 7;
+
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
@@ -225,6 +230,12 @@
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
+ case VIEW_TYPE_WORK_EDU_CARD:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.work_apps_edu, parent, false));
+ case VIEW_TYPE_WORK_DISABLED_CARD:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.work_apps_paused, parent, false));
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
if (adapterProvider != null) {
@@ -258,8 +269,12 @@
}
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
+ case VIEW_TYPE_WORK_DISABLED_CARD:
// nothing to do
break;
+ case VIEW_TYPE_WORK_EDU_CARD:
+ ((WorkEduCard) holder.itemView).setPosition(position);
+ break;
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
if (adapterProvider != null) {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index ecadec6..f3c5dd6 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -147,8 +147,7 @@
mWorkManager = new WorkProfileManager(
mActivityContext.getSystemService(UserManager.class),
- this,
- Utilities.getPrefs(mActivityContext), mActivityContext.getDeviceProfile());
+ this, Utilities.getPrefs(mActivityContext));
mAH = Arrays.asList(null, null, null);
mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
@@ -253,7 +252,7 @@
public boolean shouldContainerScroll(MotionEvent ev) {
BaseDragLayer dragLayer = mActivityContext.getDragLayer();
// Scroll if not within the container view (e.g. over large-screen scrim).
- if (!dragLayer.isEventOverView(this, ev)) {
+ if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
return true;
}
if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
@@ -304,7 +303,8 @@
mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
return true;
}
- if (isSearching()) {
+ if (isSearching()
+ && mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) {
// if in search state, consume touch event.
return true;
}
@@ -602,6 +602,8 @@
if (mAH.get(currentActivePage).mRecyclerView != null) {
mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar();
}
+ // Header keeps track of active recycler view to properly render header protection.
+ mHeader.setActiveRV(currentActivePage);
reset(true /* animate */);
mWorkManager.onActivePageChanged(currentActivePage);
@@ -799,12 +801,9 @@
mType = type;
mAppsList = new AlphabeticalAppsList<>(mActivityContext,
isSearch() ? null : mAllAppsStore,
- isWork() ? mWorkManager.getAdapterProvider() : null);
-
+ isWork() ? mWorkManager : null);
BaseAdapterProvider[] adapterProviders =
- isWork() ? new BaseAdapterProvider[]{mMainAdapterProvider,
- mWorkManager.getAdapterProvider()}
- : new BaseAdapterProvider[]{mMainAdapterProvider};
+ new BaseAdapterProvider[]{mMainAdapterProvider};
mAdapter = createAdapter(mAppsList, adapterProviders);
mAppsList.setAdapter(mAdapter);
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 6ff2132..75e527a 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -15,11 +15,8 @@
*/
package com.android.launcher3.allapps;
-import android.graphics.Rect;
import android.view.View;
-import com.android.launcher3.DeviceProfile;
-
/**
* A abstract representation of a row in all-apps view
*/
@@ -29,8 +26,6 @@
void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows, boolean tabsHidden);
- void setInsets(Rect insets, DeviceProfile grid);
-
int getExpectedHeight();
/**
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 6ecbad2..c5bdb69 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -17,6 +17,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -30,7 +31,6 @@
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder;
@@ -84,7 +84,7 @@
// These two values are necessary to ensure that the header protection is drawn correctly.
private final int mHeaderTopAdjustment;
private final int mHeaderBottomAdjustment;
- private final boolean mHeaderProtectionSupported;
+ private boolean mHeaderProtectionSupported;
protected ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
@@ -122,9 +122,14 @@
mHeaderBottomAdjustment = context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_header_bottom_adjustment);
mHeaderProtectionSupported = context.getResources().getBoolean(
- R.bool.config_header_protection_supported)
- // TODO(b/208599118) Support header protection for bottom sheet.
- && !ActivityContext.lookupContext(context).getDeviceProfile().isTablet;
+ R.bool.config_header_protection_supported);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mHeaderProtectionSupported = getContext().getResources().getBoolean(
+ R.bool.config_header_protection_supported);
}
@Override
@@ -158,22 +163,6 @@
PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mTabLayout.getLayoutParams().width = getTabWidth();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- /**
- * Returns distance between left and right app icons
- */
- public int getTabWidth() {
- DeviceProfile grid = ActivityContext.lookupContext(getContext()).getDeviceProfile();
- int totalWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
- int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
- return totalWidth - iconPadding - grid.allAppsIconDrawablePaddingPx;
- }
-
private void recreateAllRowsArray() {
int pluginCount = mPluginRows.size();
if (pluginCount == 0) {
@@ -423,15 +412,6 @@
p.y = getTop() - mCurrentRV.getTop() - ((ViewGroup) mCurrentRV.getParent()).getTop();
}
- public boolean hasVisibleContent() {
- for (FloatingHeaderRow row : mAllRows) {
- if (row.hasVisibleContent()) {
- return true;
- }
- }
- return false;
- }
-
public boolean isHeaderProtectionSupported() {
return mHeaderProtectionSupported;
}
@@ -443,10 +423,9 @@
@Override
public void setInsets(Rect insets) {
- DeviceProfile grid = ActivityContext.lookupContext(getContext()).getDeviceProfile();
- for (FloatingHeaderRow row : mAllRows) {
- row.setInsets(insets, grid);
- }
+ int leftRightPadding = ActivityContext.lookupContext(getContext())
+ .getDeviceProfile().allAppsLeftRightPadding;
+ setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
}
public <T extends FloatingHeaderRow> T findFixedRowByType(Class<T> type) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index 5b5fbb7..a9d36d1 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -18,10 +18,8 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import android.graphics.Rect;
import android.view.View;
-import com.android.launcher3.DeviceProfile;
import com.android.systemui.plugins.AllAppsRow;
/**
@@ -43,9 +41,6 @@
boolean tabsHidden) { }
@Override
- public void setInsets(Rect insets, DeviceProfile grid) { }
-
- @Override
public int getExpectedHeight() {
return mPlugin.getExpectedHeight();
}
diff --git a/src/com/android/launcher3/allapps/WorkAdapterProvider.java b/src/com/android/launcher3/allapps/WorkAdapterProvider.java
deleted file mode 100644
index 76d08c8..0000000
--- a/src/com/android/launcher3/allapps/WorkAdapterProvider.java
+++ /dev/null
@@ -1,138 +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.allapps;
-
-import android.content.SharedPreferences;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
-import com.android.launcher3.model.StringCache;
-import com.android.launcher3.views.ActivityContext;
-
-import java.util.ArrayList;
-
-/**
- * A UI expansion wrapper providing for providing work profile specific views
- */
-public class WorkAdapterProvider extends BaseAdapterProvider {
-
- public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
-
- private static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 20;
- private static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 21;
-
- @WorkProfileManager.WorkProfileState
- private int mState;
- private ActivityContext mActivityContext;
- private SharedPreferences mPreferences;
-
- WorkAdapterProvider(ActivityContext activityContext, SharedPreferences prefs) {
- mActivityContext = activityContext;
- mPreferences = prefs;
- }
-
- @Override
- public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) {
- if (holder.itemView instanceof WorkEduCard) {
- ((WorkEduCard) holder.itemView).setPosition(position);
- }
- }
-
- @Override
- public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
- ViewGroup parent, int viewType) {
- int viewId = viewType == VIEW_TYPE_WORK_DISABLED_CARD ? R.layout.work_apps_paused
- : R.layout.work_apps_edu;
- View view = layoutInflater.inflate(viewId, parent, false);
- setDeviceManagementResources(view, viewType);
- return new AllAppsGridAdapter.ViewHolder(view);
- }
-
- private void setDeviceManagementResources(View view, int viewType) {
- StringCache cache = mActivityContext.getStringCache();
- if (cache == null) {
- return;
- }
- if (viewType == VIEW_TYPE_WORK_DISABLED_CARD) {
- setWorkProfilePausedResources(view, cache);
- } else {
- setWorkProfileEduResources(view, cache);
- }
- }
-
- private void setWorkProfilePausedResources(View view, StringCache cache) {
- TextView title = view.findViewById(R.id.work_apps_paused_title);
- title.setText(cache.workProfilePausedTitle);
-
- TextView body = view.findViewById(R.id.work_apps_paused_content);
- body.setText(cache.workProfilePausedDescription);
-
- TextView button = view.findViewById(R.id.enable_work_apps);
- button.setText(cache.workProfileEnableButton);
- }
-
- private void setWorkProfileEduResources(View view, StringCache cache) {
- TextView title = view.findViewById(R.id.work_apps_paused_title);
- title.setText(cache.workProfileEdu);
-
- }
-
- /**
- * returns whether or not work apps should be visible in work tab.
- */
- public boolean shouldShowWorkApps() {
- return mState != WorkProfileManager.STATE_DISABLED;
- }
-
- /**
- * Adds work profile specific adapter items to adapterItems and returns number of items added
- */
- public int addWorkItems(ArrayList<AllAppsGridAdapter.AdapterItem> adapterItems) {
- if (mState == WorkProfileManager.STATE_DISABLED) {
- //add disabled card here.
- adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_DISABLED_CARD));
- } else if (mState == WorkProfileManager.STATE_ENABLED && !isEduSeen()) {
- adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_EDU_CARD));
- }
-
- return adapterItems.size();
- }
-
- /**
- * Sets the current state of work profile
- */
- public void updateCurrentState(@WorkProfileManager.WorkProfileState int state) {
- mState = state;
- }
-
- @Override
- public boolean isViewSupported(int viewType) {
- return viewType == VIEW_TYPE_WORK_DISABLED_CARD || viewType == VIEW_TYPE_WORK_EDU_CARD;
- }
-
- @Override
- public int getItemsPerRow(int viewType, int appsPerRow) {
- return 1;
- }
-
- private boolean isEduSeen() {
- return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
- }
-}
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index 836cd5a..968a556 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
+
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -22,9 +24,11 @@
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
+import android.widget.TextView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
/**
@@ -70,14 +74,18 @@
protected void onFinishInflate() {
super.onFinishInflate();
findViewById(R.id.action_btn).setOnClickListener(this);
- MarginLayoutParams lp = ((MarginLayoutParams) findViewById(R.id.wrapper).getLayoutParams());
- lp.width = mActivityContext.getAppsView().getFloatingHeaderView().getTabWidth();
+
+ StringCache cache = mActivityContext.getStringCache();
+ if (cache != null) {
+ TextView title = findViewById(R.id.work_apps_paused_title);
+ title.setText(cache.workProfileEdu);
+ }
}
@Override
public void onClick(View view) {
startAnimation(mDismissAnim);
- Utilities.getPrefs(getContext()).edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP,
+ Utilities.getPrefs(getContext()).edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP,
1).apply();
}
@@ -87,14 +95,10 @@
}
@Override
- public void onAnimationRepeat(Animation animation) {
-
- }
+ public void onAnimationRepeat(Animation animation) { }
@Override
- public void onAnimationStart(Animation animation) {
-
- }
+ public void onAnimationStart(Animation animation) { }
private void removeCard() {
if (mPosition == -1) {
@@ -107,8 +111,14 @@
}
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int size = MeasureSpec.getSize(widthMeasureSpec);
+ findViewById(R.id.wrapper).getLayoutParams().width = getTabWidth(getContext(), size);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
public void setPosition(int position) {
mPosition = position;
}
-
}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 733577e..aee7c4c 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -16,20 +16,23 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
+import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
import android.view.WindowInsets;
import android.widget.Button;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
@@ -50,7 +53,6 @@
private boolean mWorkEnabled;
private boolean mOnWorkTab;
-
public WorkModeSwitch(Context context) {
this(context, null, 0);
}
@@ -85,15 +87,39 @@
@Override
public void setInsets(Rect insets) {
- int bottomInset = insets.bottom - mInsets.bottom;
mInsets.set(insets);
- ViewGroup.MarginLayoutParams marginLayoutParams =
- (ViewGroup.MarginLayoutParams) getLayoutParams();
- if (marginLayoutParams != null) {
- marginLayoutParams.bottomMargin = bottomInset + marginLayoutParams.bottomMargin;
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ if (lp != null) {
+ int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom);
+ if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+ bottomMargin <<= 1; // Double margin to add space above search bar.
+ bottomMargin += getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
+ }
+
+ DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+ if (!dp.isGestureMode) {
+ if (dp.isTaskbarPresent) {
+ bottomMargin += dp.taskbarSize;
+ } else {
+ bottomMargin += insets.bottom;
+ }
+ }
+
+ lp.bottomMargin = bottomMargin;
}
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+ View parent = (View) getParent();
+ int size = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight()
+ - 2 * dp.allAppsLeftRightPadding;
+ int tabWidth = getTabWidth(getContext(), size);
+ int shift = (size - tabWidth) / 2 + dp.allAppsLeftRightPadding;
+ setTranslationX(Utilities.isRtl(getResources()) ? shift : -shift);
+ }
@Override
public void onActivePageChanged(int page) {
diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java
index 729622f..26a7803 100644
--- a/src/com/android/launcher3/allapps/WorkPausedCard.java
+++ b/src/com/android/launcher3/allapps/WorkPausedCard.java
@@ -23,9 +23,11 @@
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
/**
@@ -49,12 +51,27 @@
mActivityContext = ActivityContext.lookupContext(getContext());
}
-
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mBtn = findViewById(R.id.enable_work_apps);
mBtn.setOnClickListener(this);
+
+ StringCache cache = mActivityContext.getStringCache();
+ if (cache != null) {
+ setWorkProfilePausedResources(cache);
+ }
+ }
+
+ private void setWorkProfilePausedResources(StringCache cache) {
+ TextView title = findViewById(R.id.work_apps_paused_title);
+ title.setText(cache.workProfilePausedTitle);
+
+ TextView body = findViewById(R.id.work_apps_paused_content);
+ body.setText(cache.workProfilePausedDescription);
+
+ TextView button = findViewById(R.id.enable_work_apps);
+ button.setText(cache.workProfileEnableButton);
}
@Override
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index b70cb13..cfac985 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -26,20 +28,19 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.view.ViewGroup;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.function.Predicate;
/**
@@ -50,13 +51,12 @@
public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
private static final String TAG = "WorkProfileManager";
+ public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
public static final int STATE_ENABLED = 1;
public static final int STATE_DISABLED = 2;
public static final int STATE_TRANSITION = 3;
- private final UserManager mUserManager;
-
/**
* Work profile manager states
*/
@@ -66,26 +66,23 @@
STATE_TRANSITION
})
@Retention(RetentionPolicy.SOURCE)
- public @interface WorkProfileState {
- }
+ public @interface WorkProfileState { }
+ private final UserManager mUserManager;
private final BaseAllAppsContainerView<?> mAllApps;
- private final WorkAdapterProvider mAdapterProvider;
private final Predicate<ItemInfo> mMatcher;
private WorkModeSwitch mWorkModeSwitch;
- private final DeviceProfile mDeviceProfile;
@WorkProfileState
private int mCurrentState;
+ private SharedPreferences mPreferences;
-
- public WorkProfileManager(UserManager userManager, BaseAllAppsContainerView<?> allApps,
- SharedPreferences preferences, DeviceProfile deviceProfile) {
+ public WorkProfileManager(
+ UserManager userManager, BaseAllAppsContainerView<?> allApps, SharedPreferences prefs) {
mUserManager = userManager;
mAllApps = allApps;
- mDeviceProfile = deviceProfile;
- mAdapterProvider = new WorkAdapterProvider(allApps.mActivityContext, preferences);
+ mPreferences = prefs;
mMatcher = mAllApps.mPersonalMatcher.negate();
}
@@ -122,7 +119,6 @@
private void updateCurrentState(@WorkProfileState int currentState) {
mCurrentState = currentState;
- mAdapterProvider.updateCurrentState(currentState);
if (getAH() != null) {
getAH().mAppsList.updateAdapterItems();
}
@@ -144,24 +140,6 @@
mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
R.layout.work_mode_fab, mAllApps, false);
}
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mWorkModeSwitch.getLayoutParams();
- int workFabMarginBottom =
- mWorkModeSwitch.getResources().getDimensionPixelSize(
- R.dimen.work_fab_margin_bottom);
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
- workFabMarginBottom <<= 1; // Double margin to add space above search bar.
- workFabMarginBottom +=
- mWorkModeSwitch.getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
- }
- if (!mAllApps.mActivityContext.getDeviceProfile().isGestureMode){
- workFabMarginBottom += mAllApps.mActivityContext.getDeviceProfile().getInsets().bottom;
- }
- lp.bottomMargin = workFabMarginBottom;
- int totalScreenWidth = mDeviceProfile.widthPx;
- int personalWorkTabWidth =
- mAllApps.mActivityContext.getAppsView().getFloatingHeaderView().getTabWidth();
- lp.rightMargin = lp.leftMargin = (totalScreenWidth - personalWorkTabWidth) / 2;
if (mWorkModeSwitch.getParent() != mAllApps) {
mAllApps.addView(mWorkModeSwitch);
}
@@ -181,10 +159,6 @@
mWorkModeSwitch = null;
}
- public WorkAdapterProvider getAdapterProvider() {
- return mAdapterProvider;
- }
-
public Predicate<ItemInfo> getMatcher() {
return mMatcher;
}
@@ -201,4 +175,28 @@
public int getCurrentState() {
return mCurrentState;
}
+
+ /**
+ * returns whether or not work apps should be visible in work tab.
+ */
+ public boolean shouldShowWorkApps() {
+ return mCurrentState != WorkProfileManager.STATE_DISABLED;
+ }
+
+ /**
+ * Adds work profile specific adapter items to adapterItems and returns number of items added
+ */
+ public int addWorkItems(ArrayList<AdapterItem> adapterItems) {
+ if (mCurrentState == WorkProfileManager.STATE_DISABLED) {
+ //add disabled card here.
+ adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_DISABLED_CARD));
+ } else if (mCurrentState == WorkProfileManager.STATE_ENABLED && !isEduSeen()) {
+ adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_EDU_CARD));
+ }
+ return adapterItems.size();
+ }
+
+ private boolean isEduSeen() {
+ return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
+ }
}
diff --git a/src/com/android/launcher3/anim/AnimatedPropertySetter.java b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
index e5f5e7c..82e645a 100644
--- a/src/com/android/launcher3/anim/AnimatedPropertySetter.java
+++ b/src/com/android/launcher3/anim/AnimatedPropertySetter.java
@@ -43,9 +43,17 @@
@Override
public Animator setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
- if (view == null || view.getAlpha() == alpha) {
+ if (view == null) {
return NO_OP;
}
+
+ // Short-circuit if the view already has this alpha value, but make sure the visibility is
+ // set correctly for the requested alpha.
+ if (Float.compare(view.getAlpha(), alpha) == 0) {
+ AlphaUpdateListener.updateVisibility(view);
+ return NO_OP;
+ }
+
ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
anim.addListener(new AlphaUpdateListener(view));
anim.setInterpolator(interpolator);
@@ -89,6 +97,18 @@
return anim;
}
+ @NonNull
+ @Override
+ public <T> Animator setColor(T target, IntProperty<T> property, int value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return NO_OP;
+ }
+ Animator anim = ObjectAnimator.ofArgb(target, property, value);
+ anim.setInterpolator(interpolator);
+ add(anim);
+ return anim;
+ }
/**
* Adds a callback to be run on every frame of the animation
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index d2207f6..b0ed2d2 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -89,6 +89,16 @@
}
/**
+ * Updates a color property of the target using the provided interpolator
+ */
+ @NonNull
+ public <T> Animator setColor(T target, IntProperty<T> property, int value,
+ TimeInterpolator interpolator) {
+ property.setValue(target, value);
+ return NO_OP;
+ }
+
+ /**
* Runs the animation as part of setting the property
*/
public abstract void add(Animator animatorSet);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 33beacd..4fd13b2 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -96,6 +96,9 @@
public static final BooleanFlag ENABLE_QUICK_SEARCH = new DeviceFlag("ENABLE_QUICK_SEARCH",
true, "Use quick search behavior.");
+ public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
+ true, "Hide header on keyboard before typing in all apps");
+
public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
"COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
@@ -209,9 +212,6 @@
"ENABLE_SCRIM_FOR_APP_LAUNCH", false,
"Enables scrim during app launch animation.");
- public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
- "ENABLE_SPLIT_SELECT", true, "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");
@@ -277,6 +277,17 @@
"ENABLE_DISMISS_PREDICTION_UNDO", false,
"Show an 'Undo' snackbar when users dismiss a predicted hotseat item");
+ public static final BooleanFlag ENABLE_CACHED_WIDGET = getDebugFlag(
+ "ENABLE_CACHED_WIDGET", true,
+ "Show previously cached widgets as opposed to deferred widget where available");
+
+ public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(
+ "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
+ "Use local overrides for search request timeout");
+
+ public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
+ "CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 0264ae2..09fe740 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -363,7 +363,10 @@
// If the content is already removed, ignore
return;
}
- View newContent = getViewFromDrawable(getContext(), crossFadeDrawable);
+ ImageView newContent = getViewFromDrawable(getContext(), crossFadeDrawable);
+ // We need to fill the ImageView with the content, otherwise the shapes of the final view
+ // and the drag view might not match exactly
+ newContent.setScaleType(ImageView.ScaleType.FIT_XY);
newContent.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
newContent.layout(0, 0, mWidth, mHeight);
addViewInLayout(newContent, 0, new LayoutParams(mWidth, mHeight));
@@ -573,7 +576,7 @@
}
}
- private static View getViewFromDrawable(Context context, Drawable drawable) {
+ private static ImageView getViewFromDrawable(Context context, Drawable drawable) {
ImageView iv = new ImageView(context);
iv.setImageDrawable(drawable);
return iv;
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 6355b62..2e5f2e5 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -79,6 +79,8 @@
private int mPrevTopPadding = -1;
private Drawable mReferenceDrawable = null;
+ private int mNumOfPrevItems = 0;
+
// These hold the first page preview items
private ArrayList<PreviewItemDrawingParams> mFirstPageParams = new ArrayList<>();
// These hold the current page preview items. It is empty if the current page is the first page.
@@ -254,7 +256,6 @@
void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page);
- int prevNumItems = params.size();
// We adjust the size of the list to match the number of items in the preview.
while (items.size() < params.size()) {
@@ -278,8 +279,9 @@
mReferenceDrawable = p.drawable;
}
} else {
- FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
- numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION, null);
+ FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i,
+ mNumOfPrevItems, i, numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION,
+ null);
if (p.anim != null) {
if (p.anim.hasEqualFinalState(anim)) {
@@ -318,7 +320,9 @@
}
void updatePreviewItems(boolean animate) {
+ int numOfPrevItemsAux = mFirstPageParams.size();
buildParamsForPage(0, mFirstPageParams, animate);
+ mNumOfPrevItems = numOfPrevItemsAux;
}
void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index d5bcb0c..a8546e8 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -43,6 +43,8 @@
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
+import android.util.Size;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -53,6 +55,8 @@
import android.view.WindowManager;
import android.widget.TextClock;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
@@ -177,10 +181,12 @@
private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>();
private final AppWidgetHost mAppWidgetHost;
private final SparseIntArray mWallpaperColorResources;
+ private final SparseArray<Size> mLauncherWidgetSpanInfo;
public LauncherPreviewRenderer(Context context,
InvariantDeviceProfile idp,
- WallpaperColors wallpaperColorsOverride) {
+ WallpaperColors wallpaperColorsOverride,
+ @Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
super(context);
mUiHandler = new Handler(Looper.getMainLooper());
@@ -224,6 +230,9 @@
mHotseat = mRootView.findViewById(R.id.hotseat);
mHotseat.resetLayout(false);
+ mLauncherWidgetSpanInfo = launcherWidgetSpanInfo == null ? new SparseArray<>() :
+ launcherWidgetSpanInfo;
+
CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingPx.left,
mDp.workspacePadding.top + mDp.cellLayoutPaddingPx.top,
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index d2e4c51..0eb86b1 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -28,11 +28,9 @@
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
-import android.util.Pair;
import android.util.Property;
-import android.util.SparseArray;
-import android.view.ContextThemeWrapper;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -40,8 +38,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.Themes;
-import java.lang.ref.WeakReference;
-
/**
* Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon.
*/
@@ -61,9 +57,9 @@
};
private static final int DEFAULT_PATH_SIZE = 100;
- private static final float PROGRESS_WIDTH = 7;
- private static final float PROGRESS_GAP = 2;
private static final int MAX_PAINT_ALPHA = 255;
+ private static final int TRACK_ALPHA = (int) (0.27f * MAX_PAINT_ALPHA);
+ private static final int DISABLED_ICON_ALPHA = (int) (0.6f * MAX_PAINT_ALPHA);
private static final long DURATION_SCALE = 500;
@@ -71,13 +67,8 @@
// Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE
private static final float COMPLETE_ANIM_FRACTION = 0.3f;
- private static final int COLOR_TRACK = 0x77EEEEEE;
- private static final int COLOR_SHADOW = 0x55000000;
-
- private static final float SMALL_SCALE = 0.6f;
-
- private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache =
- new SparseArray<>();
+ private static final float SMALL_SCALE = 0.7f;
+ private static final float PROGRESS_STROKE_SCALE = 0.075f;
private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
@@ -94,7 +85,6 @@
private final Path mScaledProgressPath;
private final Paint mProgressPaint;
- private Bitmap mShadowBitmap;
private final int mIndicatorColor;
private final int mSystemAccentColor;
private final int mSystemBackgroundColor;
@@ -134,7 +124,6 @@
mScaledProgressPath = new Path();
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
mIndicatorColor = indicatorColor;
@@ -149,47 +138,22 @@
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
+
+ float progressWidth = PROGRESS_STROKE_SCALE * bounds.width();
mTmpMatrix.setScale(
- (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE,
- (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE);
- mTmpMatrix.postTranslate(
- bounds.left + PROGRESS_WIDTH + PROGRESS_GAP,
- bounds.top + PROGRESS_WIDTH + PROGRESS_GAP);
+ (bounds.width() - 2 * progressWidth) / DEFAULT_PATH_SIZE,
+ (bounds.height() - 2 * progressWidth) / DEFAULT_PATH_SIZE);
+ mTmpMatrix.postTranslate(bounds.left + progressWidth, bounds.top + progressWidth);
mShapePath.transform(mTmpMatrix, mScaledTrackPath);
- float scale = bounds.width() / DEFAULT_PATH_SIZE;
- mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale);
+ mProgressPaint.setStrokeWidth(progressWidth);
- mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(),
- (PROGRESS_GAP ) * scale);
mPathMeasure.setPath(mScaledTrackPath, true);
mTrackLength = mPathMeasure.getLength();
setInternalProgress(mInternalStateProgress);
}
- private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
- int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1);
- WeakReference<Pair<Path, Bitmap>> shadowRef = sShadowCache.get(key);
- Pair<Path, Bitmap> cache = shadowRef != null ? shadowRef.get() : null;
- Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null;
- if (shadow != null) {
- return shadow;
- }
- shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(shadow);
- mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable
- ? COLOR_SHADOW : mSystemAccentColor);
- mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor);
- mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
- c.drawPath(mScaledTrackPath, mProgressPaint);
- mProgressPaint.clearShadowLayer();
- c.setBitmap(null);
-
- sShadowCache.put(key, new WeakReference<>(Pair.create(mShapePath, shadow)));
- return shadow;
- }
-
@Override
public void drawInternal(Canvas canvas, Rect bounds) {
if (mRanFinishAnimation) {
@@ -197,12 +161,17 @@
return;
}
- // Draw track.
+ // Draw background.
+ mProgressPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ mProgressPaint.setColor(mSystemBackgroundColor);
+ canvas.drawPath(mScaledTrackPath, mProgressPaint);
+
+ // Draw track and progress.
+ mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
+ mProgressPaint.setAlpha(TRACK_ALPHA);
+ canvas.drawPath(mScaledTrackPath, mProgressPaint);
mProgressPaint.setAlpha(mTrackAlpha);
- if (mShadowBitmap != null) {
- canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
- }
canvas.drawPath(mScaledProgressPath, mProgressPaint);
int saveCount = canvas.save();
@@ -211,6 +180,11 @@
canvas.restoreToCount(saveCount);
}
+ @Override
+ protected void updateFilter() {
+ setAlpha(mIsDisabled ? DISABLED_ICON_ALPHA : MAX_PAINT_ALPHA);
+ }
+
/**
* Updates the install progress based on the level
*/
@@ -326,13 +300,12 @@
}
private static int[] getPreloadColors(Context context) {
- Context dayNightThemeContext = new ContextThemeWrapper(
- context, android.R.style.Theme_DeviceDefault_DayNight);
int[] preloadColors = new int[2];
- preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext);
- preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating(
- dayNightThemeContext);
+ preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getAttrColor(context,
+ R.attr.preloadIconAccentColor);
+ preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getAttrColor(context,
+ R.attr.preloadIconBackgroundColor);
return preloadColors;
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index fd11b37..287b976 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -22,10 +22,13 @@
import android.app.WallpaperColors;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.database.Cursor;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
+import android.util.Size;
+import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.SurfaceControlViewHost;
@@ -34,6 +37,8 @@
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -124,6 +129,45 @@
}
/**
+ * A function that queries for the launcher app widget span info
+ *
+ * @param context The context to get the content resolver from, should be related to launcher
+ * @return A SparseArray with the app widget id being the key and the span info being the values
+ */
+ @WorkerThread
+ @Nullable
+ public SparseArray<Size> getLoadedLauncherWidgetInfo(
+ @NonNull final Context context) {
+ final SparseArray<Size> widgetInfo = new SparseArray<>();
+ final String query = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ + LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+
+ try (Cursor c = context.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[] {
+ LauncherSettings.Favorites.APPWIDGET_ID,
+ LauncherSettings.Favorites.SPANX,
+ LauncherSettings.Favorites.SPANY
+ }, query, null, null)) {
+ final int appWidgetIdIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_ID);
+ final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+ final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+ while (c.moveToNext()) {
+ final int appWidgetId = c.getInt(appWidgetIdIndex);
+ final int spanX = c.getInt(spanXIndex);
+ final int spanY = c.getInt(spanYIndex);
+
+ widgetInfo.append(appWidgetId, new Size(spanX, spanY));
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error querying for launcher widget info", e);
+ return null;
+ }
+
+ return widgetInfo;
+ }
+
+ /**
* Generates the preview in background
*/
public void loadAsync() {
@@ -174,8 +218,11 @@
loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
query);
+ final SparseArray<Size> spanInfo =
+ getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
+
MAIN_EXECUTOR.execute(() -> {
- renderView(previewContext, mBgDataModel, mWidgetProvidersMap);
+ renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo);
mOnDestroyCallbacks.add(previewContext::onDestroy);
});
}
@@ -183,7 +230,8 @@
} else {
LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
if (dataModel != null) {
- MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null));
+ MAIN_EXECUTOR.execute(() -> renderView(inflationContext, dataModel, null,
+ null));
} else {
Log.e(TAG, "Model loading failed");
}
@@ -201,12 +249,13 @@
@UiThread
private void renderView(Context inflationContext, BgDataModel dataModel,
- Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
+ @Nullable final SparseArray<Size> launcherWidgetSpanInfo) {
if (mDestroyed) {
return;
}
- View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors)
- .getRenderedView(dataModel, widgetProviderInfoMap);
+ View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors,
+ launcherWidgetSpanInfo).getRenderedView(dataModel, widgetProviderInfoMap);
// This aspect scales the view to fit in the surface and centers it
final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
mHeight / (float) view.getMeasuredHeight());
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 35fcb78..46f0b0b 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -134,10 +134,13 @@
* DeviceGridState without migration, or false otherwise.
*/
public boolean isCompatible(DeviceGridState other) {
- if (this == other) return true;
- if (other == null) return false;
- return mNumHotseat == other.mNumHotseat
- && Objects.equals(mGridSizeString, other.mGridSizeString);
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+ return Objects.equals(mDbFile, other.mDbFile);
}
public Integer getColumns() {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index ef9250c..c25929a 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -56,6 +56,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -109,9 +110,8 @@
private static boolean needsToMigrate(
DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
boolean needsToMigrate = !destDeviceState.isCompatible(srcDeviceState);
- // TODO(b/198965093): Revert this change after bug is fixed
if (needsToMigrate) {
- Log.d("b/198965093", "Migration is needed. destDeviceState: " + destDeviceState
+ Log.i(TAG, "Migration is needed. destDeviceState: " + destDeviceState
+ ", srcDeviceState: " + srcDeviceState);
}
return needsToMigrate;
@@ -267,38 +267,33 @@
/** Return what's in the src but not in the dest */
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
- Set<String> destIntentSet = new HashSet<>();
- Set<Map<String, Integer>> destFolderIntentSet = new HashSet<>();
+ Map<String, Integer> destIdSet = new HashMap<>();
for (DbEntry entry : dest) {
- if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- destFolderIntentSet.add(getFolderIntents(entry));
+ String entryID = entry.getEntryMigrationId();
+ if (destIdSet.containsKey(entryID)) {
+ destIdSet.put(entryID, destIdSet.get(entryID) + 1);
} else {
- destIntentSet.add(entry.mIntent);
+ destIdSet.put(entryID, 1);
}
}
List<DbEntry> diff = new ArrayList<>();
for (DbEntry entry : src) {
- if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- if (!destFolderIntentSet.contains(getFolderIntents(entry))) {
+ String entryID = entry.getEntryMigrationId();
+ if (destIdSet.containsKey(entryID)) {
+ Integer count = destIdSet.get(entryID);
+ if (count <= 0) {
diff.add(entry);
+ destIdSet.remove(entryID);
+ } else {
+ destIdSet.put(entryID, count - 1);
}
} else {
- if (!destIntentSet.contains(entry.mIntent)) {
- diff.add(entry);
- }
+ diff.add(entry);
}
}
return diff;
}
- private static Map<String, Integer> getFolderIntents(DbEntry entry) {
- Map<String, Integer> folder = new HashMap<>();
- for (String intent : entry.mFolderItems.keySet()) {
- folder.put(intent, entry.mFolderItems.get(intent).size());
- }
- return folder;
- }
-
private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
String srcTableName, String destTableName) {
int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
@@ -780,5 +775,31 @@
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
+
+ /**
+ * This method should return an id that should be the same for two folders containing the
+ * same elements.
+ */
+ private String getFolderMigrationId() {
+ return mFolderItems.keySet().stream()
+ .map(intentString -> mFolderItems.get(intentString).size() + intentString)
+ .sorted()
+ .collect(Collectors.joining(","));
+ }
+
+ /** This id is not used in the DB is only used while doing the migration and it identifies
+ * an entry on each workspace. For example two calculator icons would have the same
+ * migration id even thought they have different database ids.
+ */
+ public String getEntryMigrationId() {
+ switch (itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ return getFolderMigrationId();
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ return mProvider;
+ default:
+ return mIntent;
+ }
+ }
}
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 489bc38..a9d272e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -57,7 +57,9 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
@@ -343,7 +345,12 @@
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
.and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
deleteAndBindComponentsRemoved(removeMatch,
- "removed because the corresponding package or component is removed");
+ "removed because the corresponding package or component is removed. "
+ + "mOp=" + mOp + " removedPackages=" + removedPackages.stream().collect(
+ Collectors.joining(",", "[", "]"))
+ + " removedComponents=" + removedComponents.stream()
+ .filter(Objects::nonNull).map(ComponentName::toShortString)
+ .collect(Collectors.joining(",", "[", "]")));
// Remove any queued items from the install queue
ItemInstallQueue.INSTANCE.get(context)
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 1681ea5..87ae890 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -271,7 +271,7 @@
} else {
lp.leftMargin = lp.rightMargin = 0;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+ lp.bottomMargin = grid.hotseatBarSizePx;
}
setLayoutParams(lp);
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 49d97d2..8e7a10c 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -72,6 +72,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -244,23 +245,32 @@
mNotificationContainer);
}
+ private void initializeSystemShortcuts(List<SystemShortcut> shortcuts) {
+ if (shortcuts.isEmpty()) {
+ return;
+ }
+ // If there is only 1 shortcut, add it to its own container so it can show text and icon
+ if (shortcuts.size() == 1) {
+ initializeSystemShortcut(R.layout.system_shortcut, this, shortcuts.get(0));
+ return;
+ }
+ mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
+ for (SystemShortcut shortcut : shortcuts) {
+ initializeSystemShortcut(
+ R.layout.system_shortcut_icon_only, mSystemShortcutContainer,
+ shortcut);
+ }
+ }
+
@TargetApi(Build.VERSION_CODES.P)
public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
- final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
+ final List<NotificationKeyData> notificationKeys, List<SystemShortcut> shortcuts) {
mNumNotifications = notificationKeys.size();
mOriginalIcon = originalIcon;
boolean hasDeepShortcuts = shortcutCount > 0;
mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width);
- // if there are deep shortcuts, we might want to increase the width of shortcuts to fit
- // horizontally laid out system shortcuts.
- if (hasDeepShortcuts) {
- mContainerWidth = Math.max(mContainerWidth,
- systemShortcuts.size() * getResources()
- .getDimensionPixelSize(R.dimen.system_shortcut_header_icon_touch_size)
- );
- }
// Add views
if (mNumNotifications > 0) {
// Add notification entries
@@ -279,6 +289,24 @@
mDeepShortcutContainer = findViewById(R.id.deep_shortcuts_container);
}
if (hasDeepShortcuts) {
+ // Remove the widget shortcut fom the list
+ List<SystemShortcut> systemShortcuts = shortcuts
+ .stream()
+ .filter(shortcut -> !(shortcut instanceof SystemShortcut.Widgets))
+ .collect(Collectors.toList());
+ Optional<SystemShortcut.Widgets> widgetShortcutOpt = shortcuts
+ .stream()
+ .filter(shortcut -> shortcut instanceof SystemShortcut.Widgets)
+ .map(SystemShortcut.Widgets.class::cast)
+ .findFirst();
+
+ // if there are deep shortcuts, we might want to increase the width of shortcuts to fit
+ // horizontally laid out system shortcuts.
+ mContainerWidth = Math.max(mContainerWidth,
+ systemShortcuts.size() * getResources()
+ .getDimensionPixelSize(R.dimen.system_shortcut_header_icon_touch_size)
+ );
+
mDeepShortcutContainer.setVisibility(View.VISIBLE);
for (int i = shortcutCount; i > 0; i--) {
@@ -288,30 +316,19 @@
}
updateHiddenShortcuts();
- if (!systemShortcuts.isEmpty()) {
- for (SystemShortcut shortcut : systemShortcuts) {
- if (shortcut instanceof SystemShortcut.Widgets) {
- if (mWidgetContainer == null) {
- mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
- this);
- }
- initializeWidgetShortcut(mWidgetContainer, shortcut);
- }
+ if (widgetShortcutOpt.isPresent()) {
+ if (mWidgetContainer == null) {
+ mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
+ this);
}
- mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
-
- for (SystemShortcut shortcut : systemShortcuts) {
- if (!(shortcut instanceof SystemShortcut.Widgets)) {
- initializeSystemShortcut(
- R.layout.system_shortcut_icon_only, mSystemShortcutContainer,
- shortcut);
- }
- }
+ initializeWidgetShortcut(mWidgetContainer, widgetShortcutOpt.get());
}
+
+ initializeSystemShortcuts(systemShortcuts);
} else {
mDeepShortcutContainer.setVisibility(View.GONE);
- if (!systemShortcuts.isEmpty()) {
- for (SystemShortcut shortcut : systemShortcuts) {
+ if (!shortcuts.isEmpty()) {
+ for (SystemShortcut shortcut : shortcuts) {
initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
}
}
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index c554d06..2158dea 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.statemanager;
+import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
import android.os.Handler;
@@ -23,6 +24,7 @@
import androidx.annotation.CallSuper;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherRootView;
import com.android.launcher3.Utilities;
@@ -87,6 +89,10 @@
if (mDeferredResumePending) {
handleDeferredResume();
}
+
+ if (state.hasFlag(FLAG_CLOSE_POPUPS)) {
+ AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE));
+ }
}
/**
diff --git a/src/com/android/launcher3/testing/HotseatCellCenterRequest.java b/src/com/android/launcher3/testing/HotseatCellCenterRequest.java
new file mode 100644
index 0000000..cbb847e
--- /dev/null
+++ b/src/com/android/launcher3/testing/HotseatCellCenterRequest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.testing;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Request object for querying a hotseat cell region in Rect.
+ */
+public class HotseatCellCenterRequest implements TestInformationRequest {
+ public final int cellInd;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(cellInd);
+ }
+
+ @Override
+ public String getRequestName() {
+ return TestProtocol.REQUEST_HOTSEAT_CELL_CENTER;
+ }
+
+ public static final Parcelable.Creator<HotseatCellCenterRequest> CREATOR =
+ new Parcelable.Creator<HotseatCellCenterRequest>() {
+
+ @Override
+ public HotseatCellCenterRequest createFromParcel(Parcel source) {
+ return new HotseatCellCenterRequest(source);
+ }
+
+ @Override
+ public HotseatCellCenterRequest[] newArray(int size) {
+ return new HotseatCellCenterRequest[size];
+ }
+ };
+
+ private HotseatCellCenterRequest(int cellInd) {
+ this.cellInd = cellInd;
+ }
+
+ private HotseatCellCenterRequest(Parcel in) {
+ this(in.readInt());
+ }
+
+ /**
+ * Create a builder for HotseatCellCenterRequest.
+ *
+ * @return HotseatCellCenterRequest builder.
+ */
+ public static HotseatCellCenterRequest.Builder builder() {
+ return new HotseatCellCenterRequest.Builder();
+ }
+
+ /**
+ * HotseatCellCenterRequest Builder.
+ */
+ public static final class Builder {
+ private int mCellInd;
+
+ private Builder() {
+ mCellInd = 0;
+ }
+
+ /**
+ * Set the index of hotseat cells.
+ */
+ public HotseatCellCenterRequest.Builder setCellInd(int i) {
+ this.mCellInd = i;
+ return this;
+ }
+
+ /**
+ * build the HotseatCellCenterRequest.
+ */
+ public HotseatCellCenterRequest build() {
+ return new HotseatCellCenterRequest(mCellInd);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 242d2d4..0334b96 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -33,6 +33,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
@@ -153,10 +154,6 @@
mDeviceProfile.isTwoPanels);
return response;
- case TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT:
- TestProtocol.sForcePauseTimeout = Long.parseLong(arg);
- return response;
-
case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
response.putBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest);
@@ -185,7 +182,7 @@
return new int[]{cellLayout.getCountX(), cellLayout.getCountY()};
});
- case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER:
+ case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER: {
final WorkspaceCellCenterRequest request = extra.getParcelable(
TestProtocol.TEST_INFO_REQUEST_FIELD);
return getLauncherUIProperty(Bundle::putParcelable, launcher -> {
@@ -197,6 +194,21 @@
cellLayout, request.cellX, request.cellY, request.spanX, request.spanY);
return new Point(cellRect.centerX(), cellRect.centerY());
});
+ }
+
+ case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: {
+ final HotseatCellCenterRequest request = extra.getParcelable(
+ TestProtocol.TEST_INFO_REQUEST_FIELD);
+ return getLauncherUIProperty(Bundle::putParcelable, launcher -> {
+ final Hotseat hotseat = launcher.getHotseat();
+ final Rect cellRect = getDescendantRectRelativeToDragLayerForCell(launcher,
+ hotseat, request.cellInd, /* cellY= */ 0,
+ /* spanX= */ 1, /* spanY= */ 1);
+ // TODO(b/234322284): return the real center point.
+ return new Point(cellRect.left + (cellRect.right - cellRect.left) / 3,
+ cellRect.top + (cellRect.bottom - cellRect.top) / 3);
+ });
+ }
case TestProtocol.REQUEST_HAS_TIS: {
response.putBoolean(
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 3a030a8..9bc9067 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -115,6 +115,8 @@
public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
+ public static final String REQUEST_HOTSEAT_CELL_CENTER = "hotseat-cell-center";
+
public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET =
"get-focused-task-height-for-tablet";
public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET =
@@ -122,9 +124,6 @@
public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
- public static Long sForcePauseTimeout;
- public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout";
-
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
@@ -139,4 +138,9 @@
public static final String MISSING_PROMISE_ICON = "b/202985412";
public static final String BAD_STATE = "b/223498680";
public static final String TASKBAR_IN_APP_STATE = "b/227657604";
+
+ public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
+ public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
+ public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running";
+ public static final String REQUEST_EMULATE_PRINT_DEVICE = "emulate-print-device";
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index db43baa..37b76fb 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -47,48 +47,88 @@
*/
public class AllAppsSwipeController extends AbstractStateChangeTouchController {
- private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f;
+ private static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
+ private static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
+ private static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = 0.1f;
+ private static final float ALL_APPS_STAGGERED_FADE_THRESHOLD = 0.5f;
- // Custom timing for NORMAL -> ALL_APPS on phones only.
- private static final float WORKSPACE_MOTION_START = 0.1667f;
- private static final float ALL_APPS_STATE_TRANSITION = 0.305f;
- private static final float ALL_APPS_FADE_END = 0.4717f;
+ public static final Interpolator ALL_APPS_SCRIM_RESPONDER =
+ Interpolators.clampToProgress(
+ LINEAR, ALL_APPS_SCRIM_VISIBLE_THRESHOLD, ALL_APPS_STAGGERED_FADE_THRESHOLD);
+ public static final Interpolator ALL_APPS_CLAMPING_RESPONDER =
+ Interpolators.clampToProgress(
+ LINEAR,
+ 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+ 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD);
+
+ // ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ----
+
+ private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
+ private static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.305f;
+ private static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
+ private static final float ALL_APPS_FADE_END_ATOMIC = 0.4717f;
private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f;
- public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER =
- Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD);
- public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER =
- Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f);
+ private static final Interpolator LINEAR_EARLY_MANUAL =
+ Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
+ private static final Interpolator STEP_TRANSITION_ATOMIC =
+ Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_ATOMIC);
+ private static final Interpolator STEP_TRANSITION_MANUAL =
+ Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
- // Custom interpolators for NORMAL -> ALL_APPS on phones only.
// The blur to All Apps is set to be complete when the interpolator is at 0.5.
- public static final Interpolator BLUR =
+ private static final Interpolator BLUR_ADJUSTED =
+ Interpolators.mapToProgress(LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS);
+ public static final Interpolator BLUR_ATOMIC =
Interpolators.clampToProgress(
- Interpolators.mapToProgress(
- LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS),
- WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator WORKSPACE_FADE =
- Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator WORKSPACE_SCALE =
+ BLUR_ADJUSTED, WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator BLUR_MANUAL =
+ Interpolators.clampToProgress(BLUR_ADJUSTED, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
+
+ public static final Interpolator WORKSPACE_FADE_ATOMIC = STEP_TRANSITION_ATOMIC;
+ public static final Interpolator WORKSPACE_FADE_MANUAL = STEP_TRANSITION_MANUAL;
+
+ public static final Interpolator WORKSPACE_SCALE_ATOMIC =
Interpolators.clampToProgress(
- EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator HOTSEAT_FADE = WORKSPACE_FADE;
- public static final Interpolator HOTSEAT_SCALE = HOTSEAT_FADE;
- public static final Interpolator HOTSEAT_TRANSLATE =
+ EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC,
+ ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator WORKSPACE_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
+
+ public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC;
+ public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL;
+
+ public static final Interpolator HOTSEAT_SCALE_ATOMIC = STEP_TRANSITION_ATOMIC;
+ public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
+
+ public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC =
Interpolators.clampToProgress(
- EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator SCRIM_FADE =
+ EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC,
+ ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL;
+
+ public static final Interpolator SCRIM_FADE_ATOMIC =
Interpolators.clampToProgress(
Interpolators.mapToProgress(LINEAR, 0f, 0.8f),
- WORKSPACE_MOTION_START, ALL_APPS_STATE_TRANSITION);
- public static final Interpolator ALL_APPS_FADE =
+ WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL;
+
+ public static final Interpolator ALL_APPS_FADE_ATOMIC =
Interpolators.clampToProgress(
- Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1.0f),
- ALL_APPS_STATE_TRANSITION, ALL_APPS_FADE_END);
- public static final Interpolator ALL_APPS_VERTICAL_PROGRESS =
+ Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1f),
+ ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC);
+ public static final Interpolator ALL_APPS_FADE_MANUAL =
+ Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f);
+
+ public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC =
Interpolators.clampToProgress(
- Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1.0f),
- ALL_APPS_STATE_TRANSITION, 1.0f);
+ Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1f),
+ ALL_APPS_STATE_TRANSITION_ATOMIC, 1f);
+ public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_MANUAL =
+ Interpolators.clampToProgress(
+ Interpolators.mapToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f),
+ ALL_APPS_STATE_TRANSITION_MANUAL, 1f);
+
+ // --------
public AllAppsSwipeController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
@@ -141,6 +181,7 @@
protected StateAnimationConfig getConfigForStates(LauncherState fromState,
LauncherState toState) {
StateAnimationConfig config = super.getConfigForStates(fromState, toState);
+ config.userControlled = true;
if (fromState == NORMAL && toState == ALL_APPS) {
applyNormalToAllAppsAnimConfig(mLauncher, config);
} else if (fromState == ALL_APPS && toState == NORMAL) {
@@ -150,36 +191,75 @@
}
/**
- * Applies Animation config values for transition from all apps to home
+ * Applies Animation config values for transition from all apps to home.
*/
public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) {
- boolean isTablet = launcher.getDeviceProfile().isTablet;
- config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
- config.setInterpolator(ANIM_ALL_APPS_FADE, isTablet
- ? FINAL_FRAME : ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
- if (!isTablet) {
- config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
+ if (launcher.getDeviceProfile().isTablet) {
+ config.setInterpolator(ANIM_SCRIM_FADE,
+ Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
+ config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
+ if (!config.userControlled) {
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
+ }
+ } else {
+ if (config.userControlled) {
+ config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL));
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ Interpolators.reverse(WORKSPACE_FADE_MANUAL));
+ config.setInterpolator(ANIM_WORKSPACE_SCALE,
+ Interpolators.reverse(WORKSPACE_SCALE_MANUAL));
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ Interpolators.reverse(HOTSEAT_FADE_MANUAL));
+ config.setInterpolator(ANIM_HOTSEAT_SCALE,
+ Interpolators.reverse(HOTSEAT_SCALE_MANUAL));
+ config.setInterpolator(ANIM_HOTSEAT_TRANSLATE,
+ Interpolators.reverse(HOTSEAT_TRANSLATE_MANUAL));
+ config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE_MANUAL));
+ config.setInterpolator(ANIM_ALL_APPS_FADE,
+ Interpolators.reverse(ALL_APPS_FADE_MANUAL));
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS,
+ Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS_MANUAL));
+ } else {
+ config.setInterpolator(ANIM_SCRIM_FADE,
+ Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_CLAMPING_RESPONDER);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE);
+ }
}
}
/**
- * Applies Animation config values for transition from home to all apps
+ * Applies Animation config values for transition from home to all apps.
*/
- public static void applyNormalToAllAppsAnimConfig(Launcher launcher,
- StateAnimationConfig config) {
+ public static void applyNormalToAllAppsAnimConfig(
+ Launcher launcher, StateAnimationConfig config) {
if (launcher.getDeviceProfile().isTablet) {
- config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
+ config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
+ if (!config.userControlled) {
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_DECELERATE);
+ }
} else {
- config.setInterpolator(ANIM_DEPTH, BLUR);
- config.setInterpolator(ANIM_WORKSPACE_FADE, WORKSPACE_FADE);
- config.setInterpolator(ANIM_WORKSPACE_SCALE, WORKSPACE_SCALE);
- config.setInterpolator(ANIM_HOTSEAT_FADE, HOTSEAT_FADE);
- config.setInterpolator(ANIM_HOTSEAT_SCALE, HOTSEAT_SCALE);
- config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, HOTSEAT_TRANSLATE);
- config.setInterpolator(ANIM_SCRIM_FADE, SCRIM_FADE);
- config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_FADE);
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, ALL_APPS_VERTICAL_PROGRESS);
+ config.setInterpolator(ANIM_DEPTH, config.userControlled ? BLUR_MANUAL : BLUR_ATOMIC);
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ config.userControlled ? WORKSPACE_FADE_MANUAL : WORKSPACE_FADE_ATOMIC);
+ config.setInterpolator(ANIM_WORKSPACE_SCALE,
+ config.userControlled ? WORKSPACE_SCALE_MANUAL : WORKSPACE_SCALE_ATOMIC);
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ config.userControlled ? HOTSEAT_FADE_MANUAL : HOTSEAT_FADE_ATOMIC);
+ config.setInterpolator(ANIM_HOTSEAT_SCALE,
+ config.userControlled ? HOTSEAT_SCALE_MANUAL : HOTSEAT_SCALE_ATOMIC);
+ config.setInterpolator(ANIM_HOTSEAT_TRANSLATE,
+ config.userControlled ? HOTSEAT_TRANSLATE_MANUAL : HOTSEAT_TRANSLATE_ATOMIC);
+ config.setInterpolator(ANIM_SCRIM_FADE,
+ config.userControlled ? SCRIM_FADE_MANUAL : SCRIM_FADE_ATOMIC);
+ config.setInterpolator(ANIM_ALL_APPS_FADE,
+ config.userControlled ? ALL_APPS_FADE_MANUAL : ALL_APPS_FADE_ATOMIC);
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS,
+ config.userControlled
+ ? ALL_APPS_VERTICAL_PROGRESS_MANUAL
+ : ALL_APPS_VERTICAL_PROGRESS_ATOMIC);
}
}
}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 6bae745..53cd416 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -38,6 +38,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BubbleTextHolder;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
/**
* Class to handle long-clicks on workspace items and start drag as a result.
@@ -51,7 +52,11 @@
ItemLongClickListener::onAllAppsItemLongClick;
private static boolean onWorkspaceItemLongClick(View v) {
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
+ if (v instanceof LauncherAppWidgetHostView) {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
+ } else {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
+ }
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index b477905..fa4eb70 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -19,6 +19,7 @@
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
+import static android.view.Gravity.LEFT;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -50,9 +51,9 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Collections;
@@ -311,7 +312,7 @@
@Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
boolean isRtl = banner.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float translationX = 0;
@@ -424,7 +425,7 @@
}
@Override
- public void updateStagedSplitIconParams(View out, float onScreenRectCenterX,
+ public void updateSplitIconParams(View out, float onScreenRectCenterX,
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
@@ -436,6 +437,28 @@
}
@Override
+ public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+ int splitInstructionsWidth, int threeButtonNavShift) {
+ out.setPivotX(0);
+ out.setPivotY(0);
+ out.setRotation(getDegreesRotated());
+ int distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_landscape);
+ // Adjust for any insets on the left edge
+ int insetCorrectionX = dp.getInsets().left;
+ // Center the view in case of unbalanced insets on top or bottom of screen
+ int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
+ out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
+ out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ + insetCorrectionY);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+ // Setting gravity to LEFT instead of the lint-recommended START because we always want this
+ // view to be screen-left when phone is in landscape, regardless of the RtL setting.
+ lp.gravity = LEFT | CENTER_VERTICAL;
+ out.setLayoutParams(lp);
+ }
+
+ @Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
@StagePosition int stagePosition, Rect out1, Rect out2) {
// In fake land/seascape, the window bounds are always top and bottom half
@@ -447,7 +470,7 @@
@Override
public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
- StagedSplitBounds splitInfo, int desiredStagePosition) {
+ SplitBounds splitInfo, int desiredStagePosition) {
float topLeftTaskPercent = splitInfo.appsStackedVertically
? splitInfo.topTaskPercent
: splitInfo.leftTaskPercent;
@@ -464,7 +487,7 @@
@Override
public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
- int parentWidth, int parentHeight, StagedSplitBounds splitBoundsConfig,
+ int parentWidth, int parentHeight, SplitBounds splitBoundsConfig,
DeviceProfile dp, boolean isRtl) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
@@ -506,7 +529,7 @@
public void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+ DeviceProfile deviceProfile, SplitBounds splitConfig) {
FrameLayout.LayoutParams primaryIconParams =
(FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
FrameLayout.LayoutParams secondaryIconParams =
@@ -515,7 +538,8 @@
// We calculate the "midpoint" of the thumbnail area, and place the icons there.
// This is the place where the thumbnail area splits by default, in a near-50/50 split.
// It is usually not exactly 50/50, due to insets/screen cutouts.
- int fullscreenInsetThickness = deviceProfile.getInsets().top;
+ int fullscreenInsetThickness = deviceProfile.getInsets().top
+ - deviceProfile.getInsets().bottom;
int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness)
/ 2);
float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index ca46aa8..6bc021b 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -36,7 +36,7 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import java.util.List;
@@ -129,12 +129,22 @@
* @param dp The device profile, used to report rotation and hardware insets.
* @param stagePosition 0 if the staging area is pinned to top/left, 1 for bottom/right.
*/
- void updateStagedSplitIconParams(View out, float onScreenRectCenterX,
+ void updateSplitIconParams(View out, float onScreenRectCenterX,
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition);
/**
+ * Sets positioning and rotation for a SplitInstructionsView.
+ * @param out The SplitInstructionsView that needs to be positioned.
+ * @param dp The device profile, used to report rotation and device type.
+ * @param splitInstructionsHeight The SplitInstructionView's height.
+ * @param splitInstructionsWidth The SplitInstructionView's width.
+ */
+ void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+ int splitInstructionsWidth, int threeButtonNavShift);
+
+ /**
* @param splitDividerSize height of split screen drag handle in portrait, width in landscape
* @param stagePosition the split position option (top/left, bottom/right) of the first
* task selected for entering split
@@ -153,12 +163,12 @@
* @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
* outRect for
*/
- void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo,
+ void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo,
@SplitConfigurationOptions.StagePosition int desiredStagePosition);
void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
int parentWidth, int parentHeight,
- StagedSplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl);
+ SplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl);
// Overview TaskMenuView methods
void setTaskIconParams(FrameLayout.LayoutParams iconParams,
@@ -166,7 +176,7 @@
void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
+ DeviceProfile deviceProfile, SplitBounds splitConfig);
/*
* The following two methods try to center the TaskMenuView in landscape by finding the center
@@ -207,7 +217,7 @@
* @return A Pair of Floats representing the proper x and y translations.
*/
Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner);
// The following are only used by TaskViewTouchHandler.
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 508823c..181dca5 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -47,11 +48,13 @@
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -314,7 +317,7 @@
@Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
float translationX = 0;
float translationY = 0;
@@ -467,7 +470,7 @@
}
@Override
- public void updateStagedSplitIconParams(View out, float onScreenRectCenterX,
+ public void updateSplitIconParams(View out, float onScreenRectCenterX,
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
@@ -494,6 +497,58 @@
}
@Override
+ public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+ int splitInstructionsWidth, int threeButtonNavShift) {
+ out.setPivotX(0);
+ out.setPivotY(0);
+ out.setRotation(getDegreesRotated());
+ int distanceToEdge;
+ if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
+ && (dp.isTwoPanels || dp.isTablet)) {
+ // If 3-button nav is active, align the splitInstructionsView with it.
+ distanceToEdge = dp.getTaskbarOffsetY()
+ + ((dp.taskbarSize - splitInstructionsHeight) / 2);
+ } else {
+ // If 3-button nav is not active, set bottom margin according to spec.
+ if (dp.isPhone) {
+ if (dp.isLandscape) {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_landscape);
+ } else {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_portrait);
+ }
+ } else if (dp.isTwoPanels) {
+ if (dp.isLandscape) {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_twopanels_landscape);
+ } else {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_twopanels_portrait);
+ }
+ } else {
+ if (dp.isLandscape) {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_tablet_landscape);
+ } else {
+ distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_tablet_portrait);
+ }
+ }
+ }
+
+ // Center the view in case of unbalanced insets on left or right of screen
+ int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
+ // Adjust for any insets on the bottom edge
+ int insetCorrectionY = dp.getInsets().bottom;
+ out.setTranslationX(insetCorrectionX + threeButtonNavShift);
+ out.setTranslationY(-distanceToEdge + insetCorrectionY);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+ lp.gravity = CENTER_HORIZONTAL | BOTTOM;
+ out.setLayoutParams(lp);
+ }
+
+ @Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
@StagePosition int stagePosition, Rect out1, Rect out2) {
int screenHeight = dp.heightPx;
@@ -525,7 +580,7 @@
@Override
public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
- StagedSplitBounds splitInfo, int desiredStagePosition) {
+ SplitBounds splitInfo, int desiredStagePosition) {
boolean isLandscape = dp.isLandscape;
float topLeftTaskPercent = splitInfo.appsStackedVertically
? splitInfo.topTaskPercent
@@ -551,7 +606,7 @@
@Override
public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
- int parentWidth, int parentHeight, StagedSplitBounds splitBoundsConfig,
+ int parentWidth, int parentHeight, SplitBounds splitBoundsConfig,
DeviceProfile dp, boolean isRtl) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
@@ -611,7 +666,7 @@
public void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+ DeviceProfile deviceProfile, SplitBounds splitConfig) {
FrameLayout.LayoutParams primaryIconParams =
(FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
FrameLayout.LayoutParams secondaryIconParams =
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 74b6a5b..339f910 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -19,6 +19,7 @@
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
+import static android.view.Gravity.RIGHT;
import static android.view.Gravity.START;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -37,8 +38,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Collections;
@@ -105,11 +107,29 @@
return new PointF(-margin, margin);
}
+ @Override
+ public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo,
+ int desiredStagePosition) {
+ float topLeftTaskPercent = splitInfo.appsStackedVertically
+ ? splitInfo.topTaskPercent
+ : splitInfo.leftTaskPercent;
+ float dividerBarPercent = splitInfo.appsStackedVertically
+ ? splitInfo.dividerHeightPercent
+ : splitInfo.dividerWidthPercent;
+ // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of
+ // the screen. This is to preserve consistency when the user rotates: From the user's POV,
+ // the primary should always be on the left.
+ if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+ outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+ } else {
+ outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+ }
+ }
@Override
public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+ int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
View[] thumbnailViews, int desiredTaskId, View banner) {
boolean isRtl = banner.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
float translationX = 0;
@@ -167,6 +187,29 @@
}
@Override
+ public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
+ int splitInstructionsWidth, int threeButtonNavShift) {
+ out.setPivotX(0);
+ out.setPivotY(0);
+ out.setRotation(getDegreesRotated());
+ int distanceToEdge = out.getResources().getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_landscape);
+ // Adjust for any insets on the right edge
+ int insetCorrectionX = dp.getInsets().right;
+ // Center the view in case of unbalanced insets on top or bottom of screen
+ int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
+ out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
+ + insetCorrectionX);
+ out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ + insetCorrectionY);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
+ // Setting gravity to RIGHT instead of the lint-recommended END because we always want this
+ // view to be screen-right when phone is in seascape, regardless of the RtL setting.
+ lp.gravity = RIGHT | CENTER_VERTICAL;
+ out.setLayoutParams(lp);
+ }
+
+ @Override
public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
@@ -179,7 +222,7 @@
public void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+ DeviceProfile deviceProfile, SplitBounds splitConfig) {
super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
primarySnapshotWidth, primarySnapshotHeight, groupedTaskViewHeight,
groupedTaskViewWidth, isRtl, deviceProfile, splitConfig);
@@ -191,7 +234,8 @@
// We calculate the "midpoint" of the thumbnail area, and place the icons there.
// This is the place where the thumbnail area splits by default, in a near-50/50 split.
// It is usually not exactly 50/50, due to insets/screen cutouts.
- int fullscreenInsetThickness = deviceProfile.getInsets().top;
+ int fullscreenInsetThickness = deviceProfile.getInsets().top
+ - deviceProfile.getInsets().bottom;
int fullscreenMidpointFromBottom = ((deviceProfile.heightPx
- fullscreenInsetThickness) / 2);
float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
@@ -208,14 +252,14 @@
if (splitConfig.initiatedFromSeascape) {
// if the split was initiated from seascape,
// the task on the right (secondary) is slightly larger
- primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+ primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+ taskIconHeight);
+ secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
} else {
// if not,
// the task on the left (primary) is slightly larger
- primaryIconView.setTranslationY(-bottomToMidpointOffset);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+ primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+ secondaryIconView.setTranslationY(-bottomToMidpointOffset);
}
primaryIconView.setLayoutParams(primaryIconParams);
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 7c73be5..15fe1d9 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -41,7 +41,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.view.Display;
import androidx.annotation.AnyThread;
@@ -55,6 +54,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@@ -66,6 +66,7 @@
public class DisplayController implements ComponentCallbacks, SafeCloseable {
private static final String TAG = "DisplayController";
+ private static final boolean DEBUG = false;
public static final MainThreadInitializedObject<DisplayController> INSTANCE =
new MainThreadInitializedObject<>(DisplayController::new);
@@ -116,8 +117,9 @@
getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
- mInfo = new Info(getDisplayInfoContext(display), display,
- wmProxy, wmProxy.estimateInternalDisplayBounds(context));
+ Context displayInfoContext = getDisplayInfoContext(display);
+ mInfo = new Info(displayInfoContext, wmProxy,
+ wmProxy.estimateInternalDisplayBounds(displayInfoContext));
}
/**
@@ -215,18 +217,18 @@
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
Info oldInfo = mInfo;
- Context displayContext = getDisplayInfoContext(display);
- Info newInfo = new Info(displayContext, display, wmProxy, oldInfo.mPerDisplayBounds);
+ Context displayInfoContext = getDisplayInfoContext(display);
+ Info newInfo = new Info(displayInfoContext, wmProxy, oldInfo.mPerDisplayBounds);
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
|| newInfo.navigationMode != oldInfo.navigationMode) {
// Cache may not be valid anymore, recreate without cache
- newInfo = new Info(displayContext, display, wmProxy,
- wmProxy.estimateInternalDisplayBounds(displayContext));
+ newInfo = new Info(displayInfoContext, wmProxy,
+ wmProxy.estimateInternalDisplayBounds(displayInfoContext));
}
int change = 0;
- if (!newInfo.displayId.equals(oldInfo.displayId)) {
+ if (!newInfo.normalizedDisplayInfo.equals(oldInfo.normalizedDisplayInfo)) {
change |= CHANGE_ACTIVE_SCREEN;
}
if (newInfo.rotation != oldInfo.rotation) {
@@ -238,34 +240,18 @@
if (newInfo.navigationMode != oldInfo.navigationMode) {
change |= CHANGE_NAVIGATION_MODE;
}
- if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
+ if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)
+ || !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) {
change |= CHANGE_SUPPORTED_BOUNDS;
-
- Point currentS = newInfo.currentSize;
- Pair<CachedDisplayInfo, WindowBounds[]> cachedBounds =
- oldInfo.mPerDisplayBounds.get(newInfo.displayId);
- Point expectedS = cachedBounds == null ? null : cachedBounds.first.size;
- if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
- Log.e("b/198965093",
- "Inconsistent number of displays"
- + "\ndisplay state: " + display.getState()
- + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
- + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
- }
- if (expectedS != null
- && (Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
- || Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y))
- && display.getState() == Display.STATE_OFF) {
- Log.e("b/198965093",
- "Display size changed while display is off, ignoring change");
- return;
- }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "handleInfoChange - change: 0b" + Integer.toBinaryString(change));
}
if (change != 0) {
mInfo = newInfo;
final int flags = change;
- MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags));
+ MAIN_EXECUTOR.execute(() -> notifyChange(displayInfoContext, flags));
}
}
@@ -283,8 +269,8 @@
public static class Info {
// Cached property
+ public final CachedDisplayInfo normalizedDisplayInfo;
public final int rotation;
- public final String displayId;
public final Point currentSize;
public final Rect cutout;
@@ -297,51 +283,66 @@
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
- private final ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds =
+ private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
new ArrayMap<>();
- public Info(Context context, Display display) {
+ public Info(Context displayInfoContext) {
/* don't need system overrides for external displays */
- this(context, display, new WindowManagerProxy(), new ArrayMap<>());
+ this(displayInfoContext, new WindowManagerProxy(), new ArrayMap<>());
}
// Used for testing
- public Info(Context context, Display display,
+ public Info(Context displayInfoContext,
WindowManagerProxy wmProxy,
- ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache) {
- CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(context, display);
+ ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
+ CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
+ normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
currentSize = displayInfo.size;
- displayId = displayInfo.id;
cutout = displayInfo.cutout;
- Configuration config = context.getResources().getConfiguration();
+ Configuration config = displayInfoContext.getResources().getConfiguration();
fontScale = config.fontScale;
densityDpi = config.densityDpi;
mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
- navigationMode = parseNavigationMode(context);
+ navigationMode = parseNavigationMode(displayInfoContext);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
- Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId);
+ WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
- WindowBounds realBounds = wmProxy.getRealBounds(context, display, displayInfo);
+ WindowBounds realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
if (cachedValue == null) {
- supportedBounds.add(realBounds);
- } else {
+ // Unexpected normalizedDisplayInfo is found, recreate the cache
+ Log.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache");
+ mPerDisplayBounds.clear();
+ mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext));
+ cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ if (cachedValue == null) {
+ Log.e(TAG, "normalizedDisplayInfo not found in estimation: "
+ + normalizedDisplayInfo);
+ supportedBounds.add(realBounds);
+ }
+ }
+
+ if (cachedValue != null) {
// Verify that the real bounds are a match
- WindowBounds expectedBounds = cachedValue.second[displayInfo.rotation];
+ WindowBounds expectedBounds = cachedValue[displayInfo.rotation];
if (!realBounds.equals(expectedBounds)) {
WindowBounds[] clone = new WindowBounds[4];
- System.arraycopy(cachedValue.second, 0, clone, 0, 4);
+ System.arraycopy(cachedValue, 0, clone, 0, 4);
clone[displayInfo.rotation] = realBounds;
- cachedValue = Pair.create(displayInfo.normalize(), clone);
- mPerDisplayBounds.put(displayId, cachedValue);
+ mPerDisplayBounds.put(normalizedDisplayInfo, clone);
}
}
mPerDisplayBounds.values().forEach(
- pair -> Collections.addAll(supportedBounds, pair.second));
- Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize);
- Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds);
+ windowBounds -> Collections.addAll(supportedBounds, windowBounds));
+ if (DEBUG) {
+ Log.d(TAG, "displayInfo: " + displayInfo);
+ Log.d(TAG, "realBounds: " + realBounds);
+ Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo);
+ mPerDisplayBounds.forEach((key, value) -> Log.d(TAG,
+ "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ }
}
/**
@@ -369,13 +370,14 @@
public void dump(PrintWriter pw) {
Info info = mInfo;
pw.println("DisplayController.Info:");
- pw.println(" id=" + info.displayId);
+ pw.println(" normalizedDisplayInfo=" + info.normalizedDisplayInfo);
pw.println(" rotation=" + info.rotation);
pw.println(" fontScale=" + info.fontScale);
pw.println(" densityDpi=" + info.densityDpi);
pw.println(" navigationMode=" + info.navigationMode.name());
pw.println(" currentSize=" + info.currentSize);
- pw.println(" supportedBounds=" + info.supportedBounds);
+ info.mPerDisplayBounds.forEach((key, value) -> pw.println(
+ " perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
}
/**
diff --git a/src/com/android/launcher3/util/HorizontalInsettableView.java b/src/com/android/launcher3/util/HorizontalInsettableView.java
index 7979bc0..486b73d 100644
--- a/src/com/android/launcher3/util/HorizontalInsettableView.java
+++ b/src/com/android/launcher3/util/HorizontalInsettableView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.util;
+import android.util.FloatProperty;
+
/**
* Allows the implementing view to add insets to the left and right.
*/
@@ -32,4 +34,22 @@
*/
void setHorizontalInsets(float insetPercentage);
+ /**
+ * Returns the width percentage to inset the content from the left and from the right. See
+ * {@link #setHorizontalInsets};
+ */
+ float getHorizontalInsets();
+
+ FloatProperty<HorizontalInsettableView> HORIZONTAL_INSETS =
+ new FloatProperty<HorizontalInsettableView>("horizontalInsets") {
+ @Override
+ public Float get(HorizontalInsettableView view) {
+ return view.getHorizontalInsets();
+ }
+
+ @Override
+ public void setValue(HorizontalInsettableView view, float insetPercentage) {
+ view.setHorizontalInsets(insetPercentage);
+ }
+ };
}
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index 11cd07c..4b46a0a 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -23,6 +23,7 @@
import com.android.launcher3.anim.AlphaUpdateListener;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -78,6 +79,29 @@
mUpdateVisibility = updateVisibility;
}
+ /**
+ * Dumps the alpha channel values to the given PrintWriter
+ *
+ * @param prefix String to be used before every line
+ * @param pw PrintWriter where the logs should be dumped
+ * @param label String used to help identify this object
+ * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
+ * in the order of the indexes they represent, starting from 0.
+ */
+ public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
+ pw.println(prefix + label);
+
+ String innerPrefix = prefix + '\t';
+ for (int i = 0; i < alphaIndexLabels.length; i++) {
+ if (i >= mMyProperties.length) {
+ pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
+ + " however there are only " + mMyProperties.length + " alpha channels.");
+ continue;
+ }
+ pw.println(innerPrefix + alphaIndexLabels[i] + "=" + getProperty(i).getValue());
+ }
+ }
+
public class AlphaProperty {
private final int mMyMask;
diff --git a/src/com/android/launcher3/util/PendingSplitSelectInfo.java b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
new file mode 100644
index 0000000..ed02465
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+
+/**
+ * Utility class to store information regarding a split select request. This includes the taskId of
+ * the originating task, plus the stage position.
+ * This information is intended to be saved across launcher instances, e.g. when Launcher needs to
+ * recover straight into a split select state.
+ */
+public class PendingSplitSelectInfo {
+
+ private final int mStagedTaskId;
+ private final int mStagePosition;
+
+ public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
+ this.mStagedTaskId = stagedTaskId;
+ this.mStagePosition = stagePosition;
+ }
+
+ public int getStagedTaskId() {
+ return mStagedTaskId;
+ }
+
+ public @StagePosition int getStagePosition() {
+ return mStagePosition;
+ }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 6a336cc..f14d985 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -96,7 +96,7 @@
*
* If you make changes here, consider making the same changes there
*/
- public static class StagedSplitBounds {
+ public static class SplitBounds {
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
/** This rect represents the actual gap between the two apps */
@@ -124,7 +124,7 @@
public final int leftTopTaskId;
public final int rightBottomTaskId;
- public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+ public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
int rightBottomTaskId) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
@@ -163,7 +163,7 @@
}
}
- public static class StagedSplitTaskPosition {
+ public static class SplitStageInfo {
public int taskId = -1;
@StagePosition
public int stagePosition = STAGE_POSITION_UNDEFINED;
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 630df7e..6945983 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -19,6 +19,10 @@
import android.view.View;
import android.view.Window;
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
@@ -31,15 +35,26 @@
public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_FULLSCREEN_TASK = 3;
- public static final int UI_STATE_ALLAPPS = 4;
public static final int FLAG_LIGHT_NAV = 1 << 0;
public static final int FLAG_DARK_NAV = 1 << 1;
public static final int FLAG_LIGHT_STATUS = 1 << 2;
public static final int FLAG_DARK_STATUS = 1 << 3;
+ /**
+ * Security type based on WifiConfiguration.KeyMgmt
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ FLAG_LIGHT_NAV,
+ FLAG_DARK_NAV,
+ FLAG_LIGHT_STATUS,
+ FLAG_DARK_STATUS,
+ })
+ public @interface SystemUiControllerFlags {}
+
private final Window mWindow;
- private final int[] mStates = new int[5];
+ private final int[] mStates = new int[4];
public SystemUiController(Window window) {
mWindow = window;
@@ -50,7 +65,7 @@
? (FLAG_LIGHT_NAV | FLAG_LIGHT_STATUS) : (FLAG_DARK_NAV | FLAG_DARK_STATUS));
}
- public void updateUiState(int uiState, int flags) {
+ public void updateUiState(int uiState, @SystemUiControllerFlags int flags) {
if (mStates[uiState] == flags) {
return;
}
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
new file mode 100644
index 0000000..140971b
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewCapture.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Base64;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.view.ViewCaptureData.ExportedData;
+import com.android.launcher3.view.ViewCaptureData.FrameData;
+import com.android.launcher3.view.ViewCaptureData.ViewNode;
+
+import java.util.concurrent.FutureTask;
+
+/**
+ * Utility class for capturing view data every frame
+ */
+public class ViewCapture implements OnDrawListener {
+
+ private static final String TAG = "ViewCapture";
+
+ private static final int MEMORY_SIZE = 2000;
+
+ private final View mRoot;
+ private final long[] mFrameTimes = new long[MEMORY_SIZE];
+ private final Node[] mNodes = new Node[MEMORY_SIZE];
+
+ private int mFrameIndex = -1;
+
+ /**
+ * @param root the root view for the capture data
+ */
+ public ViewCapture(View root) {
+ mRoot = root;
+ }
+
+ @Override
+ public void onDraw() {
+ Trace.beginSection("view_capture");
+ long now = SystemClock.elapsedRealtimeNanos();
+
+ mFrameIndex++;
+ if (mFrameIndex >= MEMORY_SIZE) {
+ mFrameIndex = 0;
+ }
+ mFrameTimes[mFrameIndex] = now;
+ mNodes[mFrameIndex] = captureView(mRoot, mNodes[mFrameIndex]);
+ Trace.endSection();
+ }
+
+ /**
+ * Creates a proto of all the data captured so far.
+ */
+ public String dumpToString() {
+ Handler handler = mRoot.getHandler();
+ if (handler == null) {
+ handler = Executors.MAIN_EXECUTOR.getHandler();
+ }
+ FutureTask<ExportedData> task = new FutureTask<>(this::dumpToProtoUI);
+ if (Looper.myLooper() == handler.getLooper()) {
+ task.run();
+ } else {
+ handler.post(task);
+ }
+ try {
+ return Base64.encodeToString(task.get().toByteArray(),
+ Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+ } catch (Exception e) {
+ Log.e(TAG, "Error capturing proto", e);
+ return "--error--";
+ }
+ }
+
+ @UiThread
+ private ExportedData dumpToProtoUI() {
+ ExportedData.Builder dataBuilder = ExportedData.newBuilder();
+ Resources res = mRoot.getResources();
+
+ int size = (mNodes[MEMORY_SIZE - 1] == null) ? mFrameIndex + 1 : MEMORY_SIZE;
+ for (int i = size - 1; i >= 0; i--) {
+ int index = (MEMORY_SIZE + mFrameIndex - i) % MEMORY_SIZE;
+ dataBuilder.addFrameData(FrameData.newBuilder()
+ .setNode(mNodes[index].toProto(res))
+ .setTimestamp(mFrameTimes[index]));
+ }
+ return dataBuilder.build();
+ }
+
+ private Node captureView(View view, Node recycle) {
+ Node result = recycle == null ? new Node() : recycle;
+
+ result.clazz = view.getClass();
+ result.hashCode = view.hashCode();
+ result.id = view.getId();
+ result.left = view.getLeft();
+ result.top = view.getTop();
+ result.right = view.getRight();
+ result.bottom = view.getBottom();
+ result.scrollX = view.getScrollX();
+ result.scrollY = view.getScrollY();
+
+ result.translateX = view.getTranslationX();
+ result.translateY = view.getTranslationY();
+ result.scaleX = view.getScaleX();
+ result.scaleY = view.getScaleY();
+ result.alpha = view.getAlpha();
+
+ result.visibility = view.getVisibility();
+ result.willNotDraw = view.willNotDraw();
+
+ if (view instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) view;
+ result.clipChildren = parent.getClipChildren();
+ int childCount = parent.getChildCount();
+ if (childCount == 0) {
+ result.children = null;
+ } else {
+ result.children = captureView(parent.getChildAt(0), result.children);
+ Node lastChild = result.children;
+ for (int i = 1; i < childCount; i++) {
+ lastChild.sibling = captureView(parent.getChildAt(i), lastChild.sibling);
+ lastChild = lastChild.sibling;
+ }
+ lastChild.sibling = null;
+ }
+ } else {
+ result.clipChildren = false;
+ result.children = null;
+ }
+ return result;
+ }
+
+ private static class Node {
+
+ // We store reference in memory to avoid generating and storing too many strings
+ public Class clazz;
+ public int hashCode;
+
+ public int id;
+ public int left, top, right, bottom;
+ public int scrollX, scrollY;
+
+ public float translateX, translateY;
+ public float scaleX, scaleY;
+ public float alpha;
+
+ public int visibility;
+ public boolean willNotDraw;
+ public boolean clipChildren;
+
+ public Node sibling;
+ public Node children;
+
+ public ViewNode toProto(Resources res) {
+ String resolvedId;
+ if (id >= 0) {
+ try {
+ resolvedId = res.getResourceTypeName(id) + '/' + res.getResourceEntryName(id);
+ } catch (Resources.NotFoundException e) {
+ resolvedId = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
+ }
+ } else {
+ resolvedId = "NO_ID";
+ }
+
+ ViewNode.Builder result = ViewNode.newBuilder()
+ .setClassname(clazz.getName() + "@" + hashCode)
+ .setId(resolvedId)
+ .setLeft(left)
+ .setTop(top)
+ .setWidth(right - left)
+ .setHeight(bottom - top)
+ .setTranslationX(translateX)
+ .setTranslationY(translateY)
+ .setScaleX(scaleX)
+ .setScaleY(scaleY)
+ .setAlpha(alpha)
+ .setVisibility(visibility)
+ .setWillNotDraw(willNotDraw)
+ .setClipChildren(clipChildren);
+ Node child = children;
+ while (child != null) {
+ result.addChildren(child.toProto(res));
+ child = child.sibling;
+ }
+ return result.build();
+ }
+
+ }
+}
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index a15679a..91480e1 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -76,6 +76,7 @@
+ "bounds=" + bounds
+ ", insets=" + insets
+ ", availableSize=" + availableSize
+ + ", rotationHint=" + rotationHint
+ '}';
}
diff --git a/src/com/android/launcher3/util/window/CachedDisplayInfo.java b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
index 06b9829..23f37aa 100644
--- a/src/com/android/launcher3/util/window/CachedDisplayInfo.java
+++ b/src/com/android/launcher3/util/window/CachedDisplayInfo.java
@@ -30,7 +30,6 @@
*/
public class CachedDisplayInfo {
- public final String id;
public final Point size;
public final int rotation;
public final Rect cutout;
@@ -40,11 +39,10 @@
}
public CachedDisplayInfo(Point size, int rotation) {
- this("", size, rotation, new Rect());
+ this(size, rotation, new Rect());
}
- public CachedDisplayInfo(String id, Point size, int rotation, Rect cutout) {
- this.id = id;
+ public CachedDisplayInfo(Point size, int rotation, Rect cutout) {
this.size = size;
this.rotation = rotation;
this.cutout = cutout;
@@ -62,16 +60,15 @@
Rect newCutout = new Rect(cutout);
rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0));
- return new CachedDisplayInfo(id, newSize, Surface.ROTATION_0, newCutout);
+ return new CachedDisplayInfo(newSize, Surface.ROTATION_0, newCutout);
}
@Override
public String toString() {
return "CachedDisplayInfo{"
- + "id='" + id + '\''
- + ", size=" + size
- + ", rotation=" + rotation
+ + "size=" + size
+ ", cutout=" + cutout
+ + ", rotation=" + rotation
+ '}';
}
@@ -80,13 +77,13 @@
if (this == o) return true;
if (!(o instanceof CachedDisplayInfo)) return false;
CachedDisplayInfo that = (CachedDisplayInfo) o;
- return rotation == that.rotation && Objects.equals(id, that.id)
- && Objects.equals(size, that.size) && Objects.equals(cutout,
- that.cutout);
+ return rotation == that.rotation
+ && Objects.equals(size, that.size)
+ && Objects.equals(cutout, that.cutout);
}
@Override
public int hashCode() {
- return Objects.hash(id, size, rotation, cutout);
+ return Objects.hash(size, rotation, cutout);
}
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 9665bf9..d5a065a 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -16,7 +16,6 @@
package com.android.launcher3.util.window;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
@@ -41,7 +40,6 @@
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.util.ArrayMap;
-import android.util.Pair;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.Surface;
@@ -88,20 +86,12 @@
* Returns a map of normalized info of internal displays to estimated window bounds
* for that display
*/
- public ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> estimateInternalDisplayBounds(
- Context context) {
- Display[] displays = getDisplays(context);
- ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> result = new ArrayMap<>();
- for (Display display : displays) {
- if (isInternalDisplay(display)) {
- Context displayContext = Utilities.ATLEAST_S
- ? context.createWindowContext(display, TYPE_APPLICATION, null)
- : context.createDisplayContext(display);
- CachedDisplayInfo info = getDisplayInfo(displayContext, display).normalize();
- WindowBounds[] bounds = estimateWindowBounds(context, info);
- result.put(info.id, Pair.create(info, bounds));
- }
- }
+ public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ Context displayInfoContext) {
+ CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize();
+ WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
+ ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ result.put(info, bounds);
return result;
}
@@ -109,12 +99,11 @@
* Returns the real bounds for the provided display after applying any insets normalization
*/
@TargetApi(Build.VERSION_CODES.R)
- public WindowBounds getRealBounds(Context windowContext,
- Display display, CachedDisplayInfo info) {
+ public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
if (!Utilities.ATLEAST_R) {
Point smallestSize = new Point();
Point largestSize = new Point();
- display.getCurrentSizeRange(smallestSize, largestSize);
+ getDisplay(displayInfoContext).getCurrentSizeRange(smallestSize, largestSize);
if (info.size.y > info.size.x) {
// Portrait
@@ -122,17 +111,16 @@
info.rotation);
} else {
// Landscape
- new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
+ return new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
info.rotation);
}
}
- WindowMetrics wm = windowContext.getSystemService(WindowManager.class)
+ WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
-
Rect insets = new Rect();
- normalizeWindowInsets(windowContext, wm.getWindowInsets(), insets);
- return new WindowBounds(wm.getBounds(), insets, info.rotation);
+ normalizeWindowInsets(displayInfoContext, windowMetrics.getWindowInsets(), insets);
+ return new WindowBounds(windowMetrics.getBounds(), insets, info.rotation);
}
/**
@@ -169,12 +157,9 @@
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars());
-
-
int statusBarHeight = getDimenByName(systemRes,
(isPortrait) ? STATUS_BAR_HEIGHT_PORTRAIT : STATUS_BAR_HEIGHT_LANDSCAPE,
STATUS_BAR_HEIGHT);
-
Insets newStatusBarInsets = Insets.of(
statusBarInsets.left,
Math.max(statusBarInsets.top, statusBarHeight),
@@ -202,21 +187,14 @@
}
/**
- * Returns true if the display is an internal displays
- */
- protected boolean isInternalDisplay(Display display) {
- return display.getDisplayId() == Display.DEFAULT_DISPLAY;
- }
-
- /**
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
- public WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo display) {
+ protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
- int rotation = display.rotation;
- Rect safeCutout = display.cutout;
+ int rotation = displayInfo.rotation;
+ Rect safeCutout = displayInfo.cutout;
- int minSize = Math.min(display.size.x, display.size.y);
+ int minSize = Math.min(displayInfo.size.x, displayInfo.size.y);
int swDp = (int) dpiFromPx(minSize, densityDpi);
Resources systemRes;
@@ -255,7 +233,7 @@
Point tempSize = new Point();
for (int i = 0; i < 4; i++) {
int rotationChange = deltaRotation(rotation, i);
- tempSize.set(display.size.x, display.size.y);
+ tempSize.set(displayInfo.size.x, displayInfo.size.y);
rotateSize(tempSize, rotationChange);
Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y);
@@ -311,55 +289,58 @@
* Returns a CachedDisplayInfo initialized for the current display
*/
@TargetApi(Build.VERSION_CODES.S)
- public CachedDisplayInfo getDisplayInfo(Context displayContext, Display display) {
- int rotation = getRotation(displayContext);
- Rect cutoutRect = new Rect();
- Point size = new Point();
+ public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) {
+ int rotation = getRotation(displayInfoContext);
if (Utilities.ATLEAST_S) {
- WindowMetrics wm = displayContext.getSystemService(WindowManager.class)
+ WindowMetrics windowMetrics = displayInfoContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
- DisplayCutout cutout = wm.getWindowInsets().getDisplayCutout();
- if (cutout != null) {
- cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
- cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
- }
-
- size.set(wm.getBounds().right, wm.getBounds().bottom);
+ return getDisplayInfo(windowMetrics, rotation);
} else {
+ Point size = new Point();
+ Display display = getDisplay(displayInfoContext);
display.getRealSize(size);
+ Rect cutoutRect = new Rect();
+ return new CachedDisplayInfo(size, rotation, cutoutRect);
}
- return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutoutRect);
}
/**
- * Returns a unique ID representing the display
+ * Returns a CachedDisplayInfo initialized for the current display
*/
- protected String getDisplayId(Display display) {
- return Integer.toString(display.getDisplayId());
+ @TargetApi(Build.VERSION_CODES.S)
+ protected CachedDisplayInfo getDisplayInfo(WindowMetrics windowMetrics, int rotation) {
+ Point size = new Point(windowMetrics.getBounds().right, windowMetrics.getBounds().bottom);
+ Rect cutoutRect = new Rect();
+ DisplayCutout cutout = windowMetrics.getWindowInsets().getDisplayCutout();
+ if (cutout != null) {
+ cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
+ cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
+ }
+ return new CachedDisplayInfo(size, rotation, cutoutRect);
}
/**
- * Returns rotation of the display associated with the context.
+ * Returns rotation of the display associated with the context, or rotation of DEFAULT_DISPLAY
+ * if the context isn't associated with a display.
*/
- public int getRotation(Context context) {
- Display d = null;
+ public int getRotation(Context displayInfoContext) {
+ return getDisplay(displayInfoContext).getRotation();
+ }
+
+ /**
+ *
+ * Returns the display associated with the context, or DEFAULT_DISPLAY if the context isn't
+ * associated with a display.
+ */
+ protected Display getDisplay(Context displayInfoContext) {
if (Utilities.ATLEAST_R) {
try {
- d = context.getDisplay();
+ return displayInfoContext.getDisplay();
} catch (UnsupportedOperationException e) {
// Ignore
}
}
- if (d == null) {
- d = context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
- }
- return d.getRotation();
- }
-
- /**
- * Returns all currently valid logical displays.
- */
- protected Display[] getDisplays(Context context) {
- return context.getSystemService(DisplayManager.class).getDisplays();
+ return displayInfoContext.getSystemService(DisplayManager.class).getDisplay(
+ DEFAULT_DISPLAY);
}
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f553fb4..800b1f6 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -22,13 +22,11 @@
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import android.annotation.TargetApi;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
@@ -550,18 +548,24 @@
}
@Override
- @TargetApi(Build.VERSION_CODES.Q)
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
Insets gestureInsets = insets.getMandatorySystemGestureInsets();
int gestureInsetBottom = gestureInsets.bottom;
+ Insets imeInset = Utilities.ATLEAST_R
+ ? insets.getInsets(WindowInsets.Type.ime())
+ : Insets.NONE;
DeviceProfile dp = mActivity.getDeviceProfile();
if (dp.isTaskbarPresent) {
// Ignore taskbar gesture insets to avoid interfering with TouchControllers.
gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarSize);
}
- mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
- gestureInsets.right, gestureInsetBottom);
+ mSystemGestureRegion.set(
+ Math.max(gestureInsets.left, imeInset.left),
+ Math.max(gestureInsets.top, imeInset.top),
+ Math.max(gestureInsets.right, imeInset.right),
+ Math.max(gestureInsetBottom, imeInset.bottom)
+ );
}
return super.dispatchApplyWindowInsets(insets);
}
diff --git a/src/com/android/launcher3/views/StickyHeaderLayout.java b/src/com/android/launcher3/views/StickyHeaderLayout.java
new file mode 100644
index 0000000..d6481a9
--- /dev/null
+++ b/src/com/android/launcher3/views/StickyHeaderLayout.java
@@ -0,0 +1,327 @@
+/*
+ * 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.views;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+
+/**
+ * A {@link LinearLayout} container which allows scrolling parts of its content based on the
+ * scroll of a different view. Views which are marked as sticky are not scrolled, giving the
+ * illusion of a sticky header.
+ */
+public class StickyHeaderLayout extends LinearLayout implements
+ RecyclerView.OnChildAttachStateChangeListener {
+
+ private static final FloatProperty<StickyHeaderLayout> SCROLL_OFFSET =
+ new FloatProperty<StickyHeaderLayout>("scrollAnimOffset") {
+ @Override
+ public void setValue(StickyHeaderLayout view, float offset) {
+ view.mScrollOffset = offset;
+ view.updateHeaderScroll();
+ }
+
+ @Override
+ public Float get(StickyHeaderLayout view) {
+ return view.mScrollOffset;
+ }
+ };
+
+ private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
+ private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
+
+ private RecyclerView mCurrentRecyclerView;
+ private EmptySpaceView mCurrentEmptySpaceView;
+
+ private float mLastScroll = 0;
+ private float mScrollOffset = 0;
+ private Animator mOffsetAnimator;
+
+ private boolean mShouldForwardToRecyclerView = false;
+ private int mHeaderHeight;
+
+ public StickyHeaderLayout(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public StickyHeaderLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+ }
+
+ public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Sets the recycler view, this sticky header should track
+ */
+ public void setCurrentRecyclerView(RecyclerView currentRecyclerView) {
+ boolean animateReset = mCurrentRecyclerView != null;
+ if (mCurrentRecyclerView != null) {
+ mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
+ }
+ mCurrentRecyclerView = currentRecyclerView;
+ mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
+ findCurrentEmptyView();
+ reset(animateReset);
+ }
+
+ public int getHeaderHeight() {
+ return mHeaderHeight;
+ }
+
+ private void updateHeaderScroll() {
+ mLastScroll = getCurrentScroll();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ MyLayoutParams lp = (MyLayoutParams) child.getLayoutParams();
+ child.setTranslationY(Math.max(mLastScroll, lp.scrollLimit));
+ }
+ }
+
+ private float getCurrentScroll() {
+ return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ mHeaderHeight = getMeasuredHeight();
+ if (mCurrentEmptySpaceView != null) {
+ mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight);
+ }
+ }
+
+ /** Resets any previous view translation. */
+ public void reset(boolean animate) {
+ if (mOffsetAnimator != null) {
+ mOffsetAnimator.cancel();
+ mOffsetAnimator = null;
+ }
+
+ mScrollOffset = 0;
+ if (!animate) {
+ updateHeaderScroll();
+ } else {
+ float startValue = mLastScroll - getCurrentScroll();
+ mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
+ mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
+ mOffsetAnimator.start();
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY))
+ || super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY)
+ || super.onTouchEvent(event);
+ }
+
+ private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
+ float dx = mCurrentRecyclerView.getLeft() - getLeft();
+ float dy = mCurrentRecyclerView.getTop() - getTop();
+ event.offsetLocation(dx, dy);
+ try {
+ return method.proxyEvent(mCurrentRecyclerView, event);
+ } finally {
+ event.offsetLocation(-dx, -dy);
+ }
+ }
+
+ @Override
+ public void onChildViewAttachedToWindow(@NonNull View view) {
+ if (view instanceof EmptySpaceView) {
+ findCurrentEmptyView();
+ }
+ }
+
+ @Override
+ public void onChildViewDetachedFromWindow(@NonNull View view) {
+ if (view == mCurrentEmptySpaceView) {
+ findCurrentEmptyView();
+ }
+ }
+
+ private void findCurrentEmptyView() {
+ if (mCurrentEmptySpaceView != null) {
+ mCurrentEmptySpaceView.setOnYChangeCallback(null);
+ mCurrentEmptySpaceView = null;
+ }
+ int childCount = mCurrentRecyclerView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View view = mCurrentRecyclerView.getChildAt(i);
+ if (view instanceof EmptySpaceView) {
+ mCurrentEmptySpaceView = (EmptySpaceView) view;
+ mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
+ mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ // Update various stick parameters
+ int count = getChildCount();
+ int stickyHeaderHeight = 0;
+ for (int i = 0; i < count; i++) {
+ View v = getChildAt(i);
+ MyLayoutParams lp = (MyLayoutParams) v.getLayoutParams();
+ if (lp.sticky) {
+ lp.scrollLimit = -v.getTop() + stickyHeaderHeight;
+ stickyHeaderHeight += v.getHeight();
+ } else {
+ lp.scrollLimit = Integer.MIN_VALUE;
+ }
+ }
+ updateHeaderScroll();
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new MyLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+ return new MyLayoutParams(lp.width, lp.height);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MyLayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof MyLayoutParams;
+ }
+
+ private static class MyLayoutParams extends LayoutParams {
+
+ public final boolean sticky;
+ public int scrollLimit;
+
+ MyLayoutParams(int width, int height) {
+ super(width, height);
+ sticky = false;
+ }
+
+ MyLayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StickyScroller_Layout);
+ sticky = a.getBoolean(R.styleable.StickyScroller_Layout_layout_sticky, false);
+ a.recycle();
+ }
+ }
+
+ private interface MotionEventProxyMethod {
+
+ boolean proxyEvent(ViewGroup view, MotionEvent event);
+ }
+
+ /**
+ * Empty view which allows listening for 'Y' changes
+ */
+ public static class EmptySpaceView extends View {
+
+ private Runnable mOnYChangeCallback;
+ private int mHeight = 0;
+
+ public EmptySpaceView(Context context) {
+ super(context);
+ animate().setUpdateListener(v -> notifyYChanged());
+ }
+
+ /**
+ * Sets the height for the empty view
+ * @return true if the height changed, false otherwise
+ */
+ public boolean setFixedHeight(int height) {
+ if (mHeight != height) {
+ mHeight = height;
+ requestLayout();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
+ }
+
+ public void setOnYChangeCallback(Runnable callback) {
+ mOnYChangeCallback = callback;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ notifyYChanged();
+ }
+
+ @Override
+ public void offsetTopAndBottom(int offset) {
+ super.offsetTopAndBottom(offset);
+ notifyYChanged();
+ }
+
+ @Override
+ public void setTranslationY(float translationY) {
+ super.setTranslationY(translationY);
+ notifyYChanged();
+ }
+
+ private void notifyYChanged() {
+ if (mOnYChangeCallback != null) {
+ mOnYChangeCallback.run();
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index fe83f3f..98a960c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.util.SparseArray;
+import android.widget.RemoteViews;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -37,6 +38,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.TestLogging;
@@ -70,13 +72,14 @@
private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
+ private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
+ private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
private final Context mContext;
private int mFlags = FLAG_STATE_IS_NORMAL;
private IntConsumer mAppWidgetRemovedCallback = null;
-
public LauncherAppWidgetHost(Context context) {
this(context, null);
}
@@ -95,6 +98,11 @@
if (mPendingViews.get(appWidgetId) != null) {
view = mPendingViews.get(appWidgetId);
mPendingViews.remove(appWidgetId);
+ } else if (mDeferredViews.get(appWidgetId) != null) {
+ // In case the widget view is deferred, we will simply return the deferred view as
+ // opposed to instantiate a new instance of LauncherAppWidgetHostView since launcher
+ // already added the former to the workspace.
+ view = mDeferredViews.get(appWidgetId);
} else {
view = new LauncherAppWidgetHostView(context);
}
@@ -120,12 +128,25 @@
// widgets upon bind anyway. See issue 14255011 for more context.
}
- // We go in reverse order and inflate any deferred widget
+ // We go in reverse order and inflate any deferred or cached widget
for (int i = mViews.size() - 1; i >= 0; i--) {
LauncherAppWidgetHostView view = mViews.valueAt(i);
if (view instanceof DeferredAppWidgetHostView) {
view.reInflate();
}
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ final int appWidgetId = mViews.keyAt(i);
+ if (view == mDeferredViews.get(appWidgetId)) {
+ // If the widget view was deferred, we'll need to call super.createView here
+ // to make the binder call to system process to fetch cumulative updates to this
+ // widget, as well as setting up this view for future updates.
+ super.createView(view.mLauncher, appWidgetId, view.getAppWidgetInfo());
+ // At this point #onCreateView should have been called, which in turn returned
+ // the deferred view. There's no reason to keep the reference anymore, so we
+ // removed it here.
+ mDeferredViews.remove(appWidgetId);
+ }
+ }
}
}
@@ -221,10 +242,28 @@
CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
return lahv;
} else if ((mFlags & FLAG_LISTENING) == 0) {
- DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
- view.setAppWidget(appWidgetId, appWidget);
- mViews.put(appWidgetId, view);
- return view;
+ // Since the launcher hasn't started listening to widget updates, we can't simply call
+ // super.createView here because the later will make a binder call to retrieve
+ // RemoteViews from system process.
+ // TODO: have launcher always listens to widget updates in background so that this
+ // check can be removed altogether.
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
+ && mCachedRemoteViews.get(appWidgetId) != null) {
+ // We've found RemoteViews from cache for this widget, so we will instantiate a
+ // widget host view and populate it with the cached RemoteViews.
+ final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
+ mDeferredViews.put(appWidgetId, view);
+ mViews.put(appWidgetId, view);
+ return view;
+ } else {
+ // When cache misses, a placeholder for the widget will be returned instead.
+ DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ mViews.put(appWidgetId, view);
+ return view;
+ }
} else {
try {
return super.createView(context, appWidgetId, appWidget);
@@ -281,6 +320,16 @@
@Override
public void clearViews() {
super.clearViews();
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ // First, we clear any previously cached content from existing widgets
+ mCachedRemoteViews.clear();
+ // Then we proceed to cache the content from the widgets
+ for (int i = 0; i < mViews.size(); i++) {
+ final int appWidgetId = mViews.keyAt(i);
+ final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+ mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
+ }
+ }
mViews.clear();
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 0865152..fc1e880 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -85,7 +86,7 @@
private Runnable mAutoAdvanceRunnable;
private long mDeferUpdatesUntilMillis = 0;
- private RemoteViews mDeferredRemoteViews;
+ RemoteViews mLastRemoteViews;
private boolean mHasDeferredColorChange = false;
private @Nullable SparseIntArray mDeferredColorChange = null;
@@ -150,11 +151,18 @@
TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
mTrackingWidgetUpdate = false;
}
- if (isDeferringUpdates()) {
- mDeferredRemoteViews = remoteViews;
- return;
+ if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+ mLastRemoteViews = remoteViews;
+ if (isDeferringUpdates()) {
+ return;
+ }
+ } else {
+ if (isDeferringUpdates()) {
+ mLastRemoteViews = remoteViews;
+ return;
+ }
+ mLastRemoteViews = null;
}
- mDeferredRemoteViews = null;
super.updateAppWidget(remoteViews);
@@ -218,8 +226,7 @@
SparseIntArray deferredColors;
boolean hasDeferredColors;
mDeferUpdatesUntilMillis = 0;
- remoteViews = mDeferredRemoteViews;
- mDeferredRemoteViews = null;
+ remoteViews = mLastRemoteViews;
deferredColors = mDeferredColorChange;
hasDeferredColors = mHasDeferredColorChange;
mDeferredColorChange = null;
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
deleted file mode 100644
index 716dcf3..0000000
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ /dev/null
@@ -1,223 +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.widget.picker;
-
-import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
-import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
-
-/**
- * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
- * vertical displacement upon scrolling.
- */
-final class SearchAndRecommendationsScrollController implements
- RecyclerView.OnChildAttachStateChangeListener {
-
- private static final FloatProperty<SearchAndRecommendationsScrollController> SCROLL_OFFSET =
- new FloatProperty<SearchAndRecommendationsScrollController>("scrollAnimOffset") {
- @Override
- public void setValue(SearchAndRecommendationsScrollController controller, float offset) {
- controller.mScrollOffset = offset;
- controller.updateHeaderScroll();
- }
-
- @Override
- public Float get(SearchAndRecommendationsScrollController controller) {
- return controller.mScrollOffset;
- }
- };
-
- private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
- private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
-
- final SearchAndRecommendationsView mContainer;
- final View mSearchBarContainer;
- final WidgetsSearchBar mSearchBar;
- final TextView mHeaderTitle;
- final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
- @Nullable final View mTabBar;
-
- private WidgetsRecyclerView mCurrentRecyclerView;
- private EmptySpaceView mCurrentEmptySpaceView;
-
- private float mLastScroll = 0;
- private float mScrollOffset = 0;
- private Animator mOffsetAnimator;
-
- private boolean mShouldForwardToRecyclerView = false;
-
- private int mHeaderHeight;
-
- SearchAndRecommendationsScrollController(
- SearchAndRecommendationsView searchAndRecommendationContainer) {
- mContainer = searchAndRecommendationContainer;
- mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
- mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
- mHeaderTitle = mContainer.findViewById(R.id.title);
- mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
- mTabBar = mContainer.findViewById(R.id.tabs);
-
- mContainer.setSearchAndRecommendationScrollController(this);
- }
-
- public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
- boolean animateReset = mCurrentRecyclerView != null;
- if (mCurrentRecyclerView != null) {
- mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
- }
- mCurrentRecyclerView = currentRecyclerView;
- mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
- findCurrentEmptyView();
- reset(animateReset);
- }
-
- public int getHeaderHeight() {
- return mHeaderHeight;
- }
-
- private void updateHeaderScroll() {
- mLastScroll = getCurrentScroll();
- mHeaderTitle.setTranslationY(mLastScroll);
- mRecommendedWidgetsTable.setTranslationY(mLastScroll);
-
- float searchYDisplacement = Math.max(mLastScroll, -mSearchBarContainer.getTop());
- mSearchBarContainer.setTranslationY(searchYDisplacement);
-
- if (mTabBar != null) {
- float tabsDisplacement = Math.max(mLastScroll, -mTabBar.getTop()
- + mSearchBarContainer.getHeight());
- mTabBar.setTranslationY(tabsDisplacement);
- }
- }
-
- private float getCurrentScroll() {
- return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
- }
-
- /**
- * Updates the scrollable header height
- *
- * @return {@code true} if the header height or dependent property changed.
- */
- public boolean updateHeaderHeight() {
- boolean hasSizeUpdated = false;
-
- int headerHeight = mContainer.getMeasuredHeight();
- if (headerHeight != mHeaderHeight) {
- mHeaderHeight = headerHeight;
- hasSizeUpdated = true;
- }
-
- if (mCurrentEmptySpaceView != null
- && mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight)) {
- hasSizeUpdated = true;
- }
- return hasSizeUpdated;
- }
-
- /** Resets any previous view translation. */
- public void reset(boolean animate) {
- if (mOffsetAnimator != null) {
- mOffsetAnimator.cancel();
- mOffsetAnimator = null;
- }
-
- mScrollOffset = 0;
- if (!animate) {
- updateHeaderScroll();
- } else {
- float startValue = mLastScroll - getCurrentScroll();
- mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
- mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
- mOffsetAnimator.start();
- }
- }
-
- /**
- * Returns {@code true} if a touch event should be intercepted by this controller.
- */
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY));
- }
-
- /**
- * Returns {@code true} if this controller has intercepted and consumed a touch event.
- */
- public boolean onTouchEvent(MotionEvent event) {
- return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY);
- }
-
- private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
- float dx = mCurrentRecyclerView.getLeft() - mContainer.getLeft();
- float dy = mCurrentRecyclerView.getTop() - mContainer.getTop();
- event.offsetLocation(dx, dy);
- try {
- return method.proxyEvent(mCurrentRecyclerView, event);
- } finally {
- event.offsetLocation(-dx, -dy);
- }
- }
-
- @Override
- public void onChildViewAttachedToWindow(@NonNull View view) {
- if (view instanceof EmptySpaceView) {
- findCurrentEmptyView();
- }
- }
-
- @Override
- public void onChildViewDetachedFromWindow(@NonNull View view) {
- if (view == mCurrentEmptySpaceView) {
- findCurrentEmptyView();
- }
- }
-
- private void findCurrentEmptyView() {
- if (mCurrentEmptySpaceView != null) {
- mCurrentEmptySpaceView.setOnYChangeCallback(null);
- mCurrentEmptySpaceView = null;
- }
- int childCount = mCurrentRecyclerView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View view = mCurrentRecyclerView.getChildAt(i);
- if (view instanceof EmptySpaceView) {
- mCurrentEmptySpaceView = (EmptySpaceView) view;
- mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
- mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
- return;
- }
- }
- }
-
- private interface MotionEventProxyMethod {
-
- boolean proxyEvent(ViewGroup view, MotionEvent event);
- }
-}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
deleted file mode 100644
index 0d7d2b5..0000000
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
+++ /dev/null
@@ -1,62 +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.widget.picker;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-/**
- * A {@link LinearLayout} container for holding search and widgets recommendation.
- *
- * <p>This class intercepts touch events and dispatch them to the right view.
- */
-public class SearchAndRecommendationsView extends LinearLayout {
- private SearchAndRecommendationsScrollController mController;
-
- public SearchAndRecommendationsView(Context context) {
- this(context, /* attrs= */ null);
- }
-
- public SearchAndRecommendationsView(Context context, AttributeSet attrs) {
- this(context, attrs, /* defStyleAttr= */ 0);
- }
-
- public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
- }
-
- public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- public void setSearchAndRecommendationScrollController(
- SearchAndRecommendationsScrollController controller) {
- mController = controller;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mController.onTouchEvent(event) || super.onTouchEvent(event);
- }
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetPagedView.java b/src/com/android/launcher3/widget/picker/WidgetPagedView.java
new file mode 100644
index 0000000..c95ec5f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetPagedView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.PagedView;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+
+/**
+ * A {@link PagedView} for showing different widgets for the personal and work profile.
+ */
+public class WidgetPagedView extends PersonalWorkPagedView {
+
+ public WidgetPagedView(Context context) {
+ this(context, null);
+ }
+
+ public WidgetPagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WidgetPagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setPageSpacing(getPaddingLeft());
+ }
+
+ @Override
+ public void getDrawingRect(Rect outRect) {
+ super.getDrawingRect(outRect);
+ outRect.left += getPaddingLeft();
+ outRect.right -= getPaddingRight();
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index a49cdc0..88d9723 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -62,11 +62,13 @@
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
+import com.android.launcher3.views.StickyHeaderLayout;
import com.android.launcher3.views.WidgetsEduView;
import com.android.launcher3.widget.BaseWidgetSheet;
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
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.util.WidgetsTableUtils;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -161,7 +163,13 @@
private boolean mIsNoWidgetsViewNeeded;
private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
private TextView mNoWidgetsView;
- private SearchAndRecommendationsScrollController mSearchScrollController;
+
+ private StickyHeaderLayout mSearchScrollView;
+ private WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+ private View mTabBar;
+ private View mSearchBarContainer;
+ private WidgetsSearchBar mSearchBar;
+ private TextView mHeaderTitle;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -214,17 +222,23 @@
}
mNoWidgetsView = findViewById(R.id.no_widgets_text);
- mSearchScrollController = new SearchAndRecommendationsScrollController(
- findViewById(R.id.search_and_recommendations_container));
- mSearchScrollController.setCurrentRecyclerView(
- findViewById(R.id.primary_widgets_list_view));
- mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
- mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+
+ mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
+ mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
+
+ mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
+ mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
+ mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+
+ mTabBar = mSearchScrollView.findViewById(R.id.tabs);
+ mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container);
+ mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar);
+ mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
onRecommendedWidgetsBound();
onWidgetsBound();
- mSearchScrollController.mSearchBar.initialize(
+ mSearchBar.initialize(
mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
setUpEducationViewsIfNeeded();
@@ -258,7 +272,7 @@
reset();
resetExpandedHeaders();
mCurrentWidgetsRecyclerView = recyclerView;
- mSearchScrollController.setCurrentRecyclerView(recyclerView);
+ mSearchScrollView.setCurrentRecyclerView(recyclerView);
}
}
@@ -285,7 +299,7 @@
mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
}
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
- mSearchScrollController.reset(/* animate= */ true);
+ mSearchScrollView.reset(/* animate= */ true);
}
@VisibleForTesting
@@ -355,8 +369,7 @@
@Override
protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
- setContentViewChildHorizontalMargin(mSearchScrollController.mContainer,
- contentHorizontalMarginInPx);
+ setContentViewChildHorizontalMargin(mSearchScrollView, contentHorizontalMarginInPx);
if (mViewPager == null) {
setContentViewChildHorizontalPadding(
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
@@ -390,16 +403,8 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mSearchScrollController.updateHeaderHeight()) {
- doMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
if (updateMaxSpansPerRow()) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (mSearchScrollController.updateHeaderHeight()) {
- doMeasure(widthMeasureSpec, heightMeasureSpec);
- }
}
}
@@ -460,7 +465,7 @@
if (mHasWorkProfile) {
mViewPager.setVisibility(VISIBLE);
- mSearchScrollController.mTabBar.setVisibility(VISIBLE);
+ mTabBar.setVisibility(VISIBLE);
AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
onActivePageChanged(mViewPager.getCurrentPage());
@@ -508,10 +513,10 @@
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
mIsInSearchMode = isInSearchMode;
if (isInSearchMode) {
- mSearchScrollController.mRecommendedWidgetsTable.setVisibility(GONE);
+ mRecommendedWidgetsTable.setVisibility(GONE);
if (mHasWorkProfile) {
mViewPager.setVisibility(GONE);
- mSearchScrollController.mTabBar.setVisibility(GONE);
+ mTabBar.setVisibility(GONE);
} else {
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
}
@@ -539,7 +544,6 @@
}
List<WidgetItem> recommendedWidgets =
mActivityContext.getPopupDataProvider().getRecommendedWidgets();
- WidgetsRecommendationTableLayout table = mSearchScrollController.mRecommendedWidgetsTable;
if (recommendedWidgets.size() > 0) {
float noWidgetsViewHeight = 0;
if (mIsNoWidgetsViewNeeded) {
@@ -562,9 +566,10 @@
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
recommendedWidgets, mMaxSpansPerRow);
- table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
+ mRecommendedWidgetsTable.setRecommendedWidgets(
+ recommendedWidgetsInTable, maxTableHeight);
} else {
- table.setVisibility(GONE);
+ mRecommendedWidgetsTable.setVisibility(GONE);
}
}
@@ -619,10 +624,9 @@
mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
}
- if (mSearchScrollController.mSearchBar.isSearchBarFocused()
- && !getPopupContainer().isEventOverView(
- mSearchScrollController.mSearchBarContainer, ev)) {
- mSearchScrollController.mSearchBar.clearSearchBarFocus();
+ if (mSearchBar.isSearchBarFocused()
+ && !getPopupContainer().isEventOverView(mSearchBarContainer, ev)) {
+ mSearchBar.clearSearchBarFocus();
}
}
return super.onControllerInterceptTouchEvent(ev);
@@ -663,8 +667,8 @@
@Override
public int getHeaderViewHeight() {
- return measureHeightWithVerticalMargins(mSearchScrollController.mHeaderTitle)
- + measureHeightWithVerticalMargins(mSearchScrollController.mSearchBarContainer);
+ return measureHeightWithVerticalMargins(mHeaderTitle)
+ + measureHeightWithVerticalMargins(mSearchBarContainer);
}
/** private the height, in pixel, + the vertical margins of a given view. */
@@ -681,14 +685,14 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (mIsInSearchMode) {
- mSearchScrollController.mSearchBar.reset();
+ mSearchBar.reset();
}
}
@Override
public boolean onBackPressed() {
if (mIsInSearchMode) {
- mSearchScrollController.mSearchBar.reset();
+ mSearchBar.reset();
return true;
}
return super.onBackPressed();
@@ -701,10 +705,9 @@
}
@Nullable private View getViewToShowEducationTip() {
- if (mSearchScrollController.mRecommendedWidgetsTable.getVisibility() == VISIBLE
- && mSearchScrollController.mRecommendedWidgetsTable.getChildCount() > 0) {
- return ((ViewGroup) mSearchScrollController.mRecommendedWidgetsTable.getChildAt(0))
- .getChildAt(0);
+ if (mRecommendedWidgetsTable.getVisibility() == VISIBLE
+ && mRecommendedWidgetsTable.getChildCount() > 0) {
+ return ((ViewGroup) mRecommendedWidgetsTable.getChildAt(0)).getChildAt(0);
}
AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
@@ -801,7 +804,7 @@
}
private int getEmptySpaceHeight() {
- return mSearchScrollController.getHeaderHeight();
+ return mSearchScrollView.getHeaderHeight();
}
void setup(WidgetsRecyclerView recyclerView) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 0e5a7d7..e6b9dca 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
import android.content.Context;
-import android.graphics.Rect;
import android.os.Process;
import android.util.Log;
import android.util.SparseArray;
@@ -36,7 +35,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
-import androidx.recyclerview.widget.RecyclerView.LayoutParams;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.launcher3.R;
@@ -80,10 +78,10 @@
private static final boolean DEBUG = false;
/** Uniquely identifies widgets list view type within the app. */
- private static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
- private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
- 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;
+ public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
+ public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
+ public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
+ public static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
private final Context mContext;
private final WidgetsDiffReporter mDiffReporter;
@@ -103,7 +101,6 @@
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
@Nullable private PackageUserKey mPendingClickHeader;
- private final int mSpacingBetweenEntries;
private int mMaxSpanSize = 4;
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
@@ -133,28 +130,11 @@
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SPACE,
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
- mSpacingBetweenEntries =
- context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
mRecyclerView = recyclerView;
-
- mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
- @Override
- public void getItemOffsets(
- @NonNull Rect outRect,
- @NonNull View view,
- @NonNull RecyclerView parent,
- @NonNull RecyclerView.State state) {
- super.getItemOffsets(outRect, view, parent, state);
- int position = ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
- boolean isHeader =
- view.getTag(R.id.tag_widget_entry) instanceof WidgetsListBaseEntry.Header;
- outRect.top += position > 0 && isHeader ? mSpacingBetweenEntries : 0;
- }
- });
}
@Override
@@ -286,7 +266,6 @@
listPos |= POSITION_LAST;
}
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
- holder.itemView.setTag(R.id.tag_widget_entry, entry);
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
index c61e3a4..984a274 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
@@ -27,6 +27,7 @@
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
@@ -40,6 +41,8 @@
private final float mMiddleCornerRadius;
private final ColorStateList mSurfaceColor;
private final ColorStateList mRippleColor;
+ private final int mVerticalPadding;
+ private final int mHeaderMargin;
WidgetsListDrawableFactory(Context context) {
Resources res = context.getResources();
@@ -48,6 +51,9 @@
mSurfaceColor = context.getColorStateList(R.color.surface);
mRippleColor = ColorStateList.valueOf(
Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
+ mVerticalPadding =
+ res.getDimensionPixelSize(R.dimen.widget_list_header_view_vertical_padding);
+ mHeaderMargin = res.getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
/**
@@ -74,7 +80,10 @@
stateList.addState(
LAST.mStateSet,
createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius));
- return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+ RippleDrawable ripple =
+ new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+ ripple.setPadding(0, mVerticalPadding, 0, mVerticalPadding);
+ return new InsetDrawable(ripple, 0, mHeaderMargin, 0, 0);
}
/**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index bdf646b..35fa7a4 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,24 +19,15 @@
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.SparseIntArray;
import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TableLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
-import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.widget.model.WidgetListSpaceEntry;
-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;
-import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
/**
* The widgets recycler view.
@@ -51,12 +42,13 @@
private boolean mTouchDownOnScroller;
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
- // Cached sizes
- private int mLastVisibleWidgetContentTableHeight = 0;
- private int mWidgetHeaderHeight = 0;
- private int mWidgetEmptySpaceHeight = 0;
-
- private final int mSpacingBetweenEntries;
+ /**
+ * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
+ * the size can be used for all other items of same type. Caching the last know size for
+ * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
+ * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
+ */
+ private final SparseIntArray mCachedSizes = new SparseIntArray();
public WidgetsRecyclerView(Context context) {
this(context, null);
@@ -71,13 +63,6 @@
super(context, attrs, defStyleAttr);
mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
addOnItemTouchListener(this);
-
- ActivityContext activity = ActivityContext.lookupContext(getContext());
- DeviceProfile grid = activity.getDeviceProfile();
-
- // The spacing used between entries.
- mSpacingBetweenEntries =
- getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
}
@Override
@@ -138,67 +123,6 @@
synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
}
- @Override
- public int getCurrentScrollY() {
- // Skip early if widgets are not bound.
- if (isModelNotReady() || getChildCount() == 0) {
- return -1;
- }
-
- int rowIndex = -1;
- View child = null;
-
- LayoutManager layoutManager = getLayoutManager();
- if (layoutManager instanceof LinearLayoutManager) {
- // Use the LayoutManager as the source of truth for visible positions. During
- // animations, the view group child may not correspond to the visible views that appear
- // at the top.
- rowIndex = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
- child = layoutManager.findViewByPosition(rowIndex);
- }
-
- if (child == null) {
- // If the layout manager returns null for any reason, which can happen before layout
- // has occurred for the position, then look at the child of this view as a ViewGroup.
- child = getChildAt(0);
- rowIndex = getChildPosition(child);
- }
-
- for (int i = 0; i < getChildCount(); i++) {
- View view = getChildAt(i);
- if (view instanceof TableLayout) {
- // This assumes there is ever only one content shown in this recycler view.
- mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
- } else if (view instanceof WidgetsListHeader
- && mWidgetHeaderHeight == 0
- && view.getMeasuredHeight() > 0) {
- // This assumes all header views are of the same height.
- mWidgetHeaderHeight = view.getMeasuredHeight();
- } else if (view instanceof EmptySpaceView && view.getMeasuredHeight() > 0) {
- mWidgetEmptySpaceHeight = view.getMeasuredHeight();
- }
- }
-
- int scrollPosition = getItemsHeight(rowIndex);
- int offset = getLayoutManager().getDecoratedTop(child);
-
- return getPaddingTop() + scrollPosition - offset;
- }
-
- /**
- * Returns the available scroll height, in pixel.
- *
- * <p>If the recycler view can't be scrolled, returns 0.
- */
- @Override
- protected int getAvailableScrollHeight() {
- // AvailableScrollHeight = Total height of the all items - first page height
- int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ mAdapter.getItemCount());
- int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
- return Math.max(0, availableScrollHeight);
- }
-
private boolean isModelNotReady() {
return mAdapter.getItemCount() == 0;
}
@@ -246,28 +170,28 @@
* <p>If the untilIndex is larger than the total number of items in this adapter, returns the
* sum of all items' height.
*/
- private int getItemsHeight(int untilIndex) {
+ @Override
+ protected int getItemsHeight(int untilIndex) {
+ // Initialize cache
+ int childCount = getChildCount();
+ int startPosition;
+ if (childCount > 0
+ && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
+ int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
+ for (int i = 0; i < loopCount; i++) {
+ mCachedSizes.put(
+ mAdapter.getItemViewType(startPosition + i),
+ getChildAt(i).getMeasuredHeight());
+ }
+ }
+
if (untilIndex > mAdapter.getItems().size()) {
untilIndex = mAdapter.getItems().size();
}
int totalItemsHeight = 0;
for (int i = 0; i < untilIndex; i++) {
- WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
- if (entry instanceof WidgetsListHeaderEntry
- || entry instanceof WidgetsListSearchHeaderEntry) {
- totalItemsHeight += mWidgetHeaderHeight;
- if (i > 0) {
- // Each header contains the spacing between entries as top decoration, except
- // the first one.
- totalItemsHeight += mSpacingBetweenEntries;
- }
- } else if (entry instanceof WidgetsListContentEntry) {
- totalItemsHeight += mLastVisibleWidgetContentTableHeight;
- } else if (entry instanceof WidgetListSpaceEntry) {
- totalItemsHeight += mWidgetEmptySpaceHeight;
- } else {
- throw new UnsupportedOperationException("Can't estimate height for " + entry);
- }
+ int type = mAdapter.getItemViewType(i);
+ totalItemsHeight += mCachedSizes.get(type);
}
return totalItemsHeight;
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
index 1aa5753..0c4f7aa 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
@@ -15,16 +15,12 @@
*/
package com.android.launcher3.widget.picker;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
-import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.views.StickyHeaderLayout.EmptySpaceView;
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
import java.util.List;
@@ -52,64 +48,4 @@
@ListPosition int position, List<Object> payloads) {
((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt());
}
-
- /**
- * Empty view which allows listening for 'Y' changes
- */
- public static class EmptySpaceView extends View {
-
- private Runnable mOnYChangeCallback;
- private int mHeight = 0;
-
- private EmptySpaceView(Context context) {
- super(context);
- animate().setUpdateListener(v -> notifyYChanged());
- }
-
- /**
- * Sets the height for the empty view
- * @return true if the height changed, false otherwise
- */
- public boolean setFixedHeight(int height) {
- if (mHeight != height) {
- mHeight = height;
- requestLayout();
- return true;
- }
- return false;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
- }
-
- public void setOnYChangeCallback(Runnable callback) {
- mOnYChangeCallback = callback;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- notifyYChanged();
- }
-
- @Override
- public void offsetTopAndBottom(int offset) {
- super.offsetTopAndBottom(offset);
- notifyYChanged();
- }
-
- @Override
- public void setTranslationY(float translationY) {
- super.setTranslationY(translationY);
- notifyYChanged();
- }
-
- private void notifyYChanged() {
- if (mOnYChangeCallback != null) {
- mOnYChangeCallback.run();
- }
- }
- }
}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index 11185fb..49db2a0 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -23,7 +23,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.views.ActivityContext;
/**
* Supports two indicator colors, dedicated for personal and work tabs.
@@ -72,6 +74,26 @@
return false;
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (getPaddingLeft() == 0 && getPaddingRight() == 0) {
+ // If any padding is not specified, restrict the width to emulate padding
+ int size = MeasureSpec.getSize(widthMeasureSpec);
+ size = getTabWidth(getContext(), size);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Returns distance between left and right app icons
+ */
+ public static int getTabWidth(Context context, int totalWidth) {
+ DeviceProfile grid = ActivityContext.lookupContext(context).getDeviceProfile();
+ int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
+ return totalWidth - iconPadding;
+ }
+
/**
* Interface definition for a callback to be invoked when an active page has been changed.
*/
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 6715749..c8b5e2f 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,7 +17,6 @@
package com.android.launcher3.uioverrides;
import android.app.Person;
-import android.content.Context;
import android.content.pm.ShortcutInfo;
import com.android.launcher3.Utilities;
@@ -29,11 +28,4 @@
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}
-
- /**
- * Returns the minimum space that should be left empty at the end of hotseat
- */
- public static int getHotseatEndOffset(Context context) {
- return 0;
- }
}
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 6d0fcb6..7465db5 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -23,6 +23,8 @@
import org.junit.Before
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
import org.mockito.Mockito.`when` as whenever
abstract class DeviceProfileBaseTest {
@@ -55,8 +57,9 @@
isGestureMode
)
- protected fun initializeVarsForPhone(isLandscape: Boolean = false) {
- val (x, y) = if (isLandscape)
+ protected fun initializeVarsForPhone(isGestureMode: Boolean = true,
+ isVerticalBar: Boolean = false) {
+ val (x, y) = if (isVerticalBar)
Pair(3120, 1440)
else
Pair(1440, 3120)
@@ -65,11 +68,19 @@
whenever(info.isTablet(any())).thenReturn(false)
whenever(info.getDensityDpi()).thenReturn(560)
+ whenever(info.smallestSizeDp(any())).thenReturn(411f)
- inv = newScalableInvariantDeviceProfile()
+ this.isGestureMode = isGestureMode
+
+ inv = newScalableInvariantDeviceProfile().apply {
+ deviceType = InvariantDeviceProfile.TYPE_PHONE
+ transposeLayoutWithOrientation = isVerticalBar
+ }
}
- protected fun initializeVarsForTablet(isLandscape: Boolean = false) {
+ protected fun initializeVarsForTablet(isLandscape: Boolean = false,
+ isTwoPanel: Boolean = false,
+ isGestureMode: Boolean = true) {
val (x, y) = if (isLandscape)
Pair(2560, 1600)
else
@@ -79,8 +90,21 @@
whenever(info.isTablet(any())).thenReturn(true)
whenever(info.getDensityDpi()).thenReturn(320)
+ whenever(info.smallestSizeDp(any())).thenReturn(800f)
- inv = newScalableInvariantDeviceProfile()
+ this.isGestureMode = isGestureMode
+ useTwoPanels = isTwoPanel
+
+ inv = newScalableInvariantDeviceProfile().apply {
+ deviceType = if (isTwoPanel)
+ InvariantDeviceProfile.TYPE_MULTI_DISPLAY else InvariantDeviceProfile.TYPE_TABLET
+ inlineQsb = booleanArrayOf(
+ false,
+ isLandscape,
+ false,
+ false
+ )
+ }
}
/**
@@ -110,6 +134,9 @@
).toTypedArray()
hotseatBorderSpaces = FloatArray(4) { 16f }
hotseatColumnSpan = IntArray(4) { 4 }
+ hotseatBarBottomSpace = FloatArray(4) { 48f }
+ inlineNavButtonsEndSpacing = R.dimen.taskbar_button_margin_4_4
+ hotseatQsbSpace = FloatArray(4) { 36f }
iconSize = FloatArray(4) { 56f }
allAppsIconSize = FloatArray(4) { 56f }
iconTextSize = FloatArray(4) { 14f }
@@ -133,4 +160,12 @@
false
)
}
+
+ fun dump(dp: DeviceProfile): StringWriter {
+ val stringWriter = StringWriter()
+ val printWriter = PrintWriter(stringWriter)
+ dp.dump("", printWriter)
+ printWriter.flush()
+ return stringWriter
+ }
}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt b/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt
deleted file mode 100644
index 80259a5..0000000
--- a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3
-
-import android.graphics.PointF
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.util.WindowBounds
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.`when` as whenever
-
-/**
- * Test for [DeviceProfile] grid dimensions.
- *
- * This includes workspace, cell layout, shortcut and widget container, cell sizes, etc.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileGridDimensionsTest : DeviceProfileBaseTest() {
-
- @Test
- fun getCellLayoutWidth_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() {
- val tabletWidth = 2560
- val tabletHeight = 1600
- val availableWidth = 2560
- val availableHeight = 1500
- windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
- useTwoPanels = true
- whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
- whenever(info.densityDpi).thenReturn(320)
- inv = newScalableInvariantDeviceProfile()
-
- val dp = newDP()
-
- val expectedWorkspaceWidth = availableWidth
- val expectedCellLayoutWidth =
- (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) /
- dp.panelCount
- assertThat(dp.cellLayoutWidth).isEqualTo(expectedCellLayoutWidth)
- }
-
- @Test
- fun getCellLayoutHeight_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelHeight() {
- val tabletWidth = 2560
- val tabletHeight = 1600
- val availableWidth = 2560
- val availableHeight = 1500
- windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
- useTwoPanels = true
- whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
- whenever(info.densityDpi).thenReturn(320)
- inv = newScalableInvariantDeviceProfile()
-
- val dp = newDP()
-
- val expectedWorkspaceHeight = availableHeight
- val expectedCellLayoutHeight =
- expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom)
- assertThat(dp.cellLayoutHeight).isEqualTo(expectedCellLayoutHeight)
- }
-
- @Test
- fun getCellSize_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() {
- val tabletWidth = 2560
- val tabletHeight = 1600
- val availableWidth = 2560
- val availableHeight = 1500
- windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
- useTwoPanels = true
- whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
- whenever(info.densityDpi).thenReturn(320)
- inv = newScalableInvariantDeviceProfile()
-
- val dp = newDP()
-
- val expectedWorkspaceWidth = availableWidth
- val expectedCellLayoutWidth =
- (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) /
- dp.panelCount
- val expectedShortcutAndWidgetContainerWidth =
- expectedCellLayoutWidth -
- (dp.cellLayoutPaddingPx.left + dp.cellLayoutPaddingPx.right)
- assertThat(dp.getCellSize().x).isEqualTo(
- (expectedShortcutAndWidgetContainerWidth -
- ((inv!!.numColumns - 1) * dp.cellLayoutBorderSpacePx.x)) / inv!!.numColumns)
- val expectedWorkspaceHeight = availableHeight
- val expectedCellLayoutHeight =
- expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom)
- val expectedShortcutAndWidgetContainerHeight = expectedCellLayoutHeight -
- (dp.cellLayoutPaddingPx.top + dp.cellLayoutPaddingPx.bottom)
- assertThat(dp.getCellSize().y).isEqualTo(
- (expectedShortcutAndWidgetContainerHeight -
- ((inv!!.numRows - 1) * dp.cellLayoutBorderSpacePx.y)) / inv!!.numRows)
- }
-
- @Test
- fun getPanelCount_twoPanelLandscapeScalable4By4GridTablet_equalsTwoPanels() {
- val tabletWidth = 2560
- val tabletHeight = 1600
- val availableWidth = 2560
- val availableHeight = 1500
- windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
- useTwoPanels = true
- whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
- whenever(info.densityDpi).thenReturn(320)
- inv = newScalableInvariantDeviceProfile()
-
- val dp = newDP()
-
- assertThat(dp.panelCount).isEqualTo(2)
- }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
index 593239d..5dabb33 100644
--- a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
+++ b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
@@ -32,33 +32,6 @@
class HotseatShownIconsTest : DeviceProfileBaseTest() {
@Test
- fun hotseat_size_is_normal_for_handhelds() {
- initializeVarsForPhone()
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_PHONE
- }
-
- val dp = newDP()
-
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(4)
- }
-
- @Test
- fun hotseat_size_is_max_when_large_screen() {
- initializeVarsForTablet(isLandscape = true)
- inv = newScalableInvariantDeviceProfile().apply {
- deviceType = TYPE_MULTI_DISPLAY
- }
- useTwoPanels = true
-
- val dp = newDP()
-
- assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.numShownHotseatIcons).isEqualTo(6)
- }
-
- @Test
fun hotseat_size_is_shrunk_if_needed_when_large_screen() {
initializeVarsForTablet(isLandscape = true)
inv = newScalableInvariantDeviceProfile().apply {
diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt
deleted file mode 100644
index 905c1e1..0000000
--- a/tests/src/com/android/launcher3/InlineQsbTest.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test for [DeviceProfile]
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class InlineQsbTest : DeviceProfileBaseTest() {
-
- @Test
- fun qsb_is_not_inline_for_phones() {
- initializeVarsForPhone()
-
- val dp = newDP()
-
- assertThat(dp.isQsbInline).isFalse()
- }
-
- @Test
- fun qsb_is_inline_for_tablet_portrait() {
- initializeVarsForTablet()
- inv = newScalableInvariantDeviceProfile().apply {
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- }
-
- val dp = DeviceProfile(
- context,
- inv,
- info,
- windowBounds,
- isMultiWindowMode,
- transposeLayoutWithOrientation,
- useTwoPanels,
- isGestureMode
- )
-
- assertThat(dp.isQsbInline).isFalse()
- }
-
- @Test
- fun qsb_is_inline_for_tablet_landscape() {
- initializeVarsForTablet(isLandscape = true)
- inv = newScalableInvariantDeviceProfile().apply {
- inlineQsb = booleanArrayOf(
- false,
- true, // landscape
- false,
- false
- )
- numColumns = 6
- numRows = 5
- numShownHotseatIcons = 6
- }
-
- val dp = newDP()
-
- if (dp.hotseatQsbHeight > 0) {
- assertThat(dp.isQsbInline).isTrue()
- } else { // Launcher3 doesn't have QSB height
- assertThat(dp.isQsbInline).isFalse()
- }
- }
-
- /**
- * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support
- */
- @Test
- fun qsb_is_not_inline_for_tablet_landscape_without_inline() {
- initializeVarsForTablet(isLandscape = true)
- useTwoPanels = true
-
- val dp = newDP()
-
- assertThat(dp.isQsbInline).isFalse()
- }
-
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
new file mode 100644
index 0000000..3ca05bc
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+
+public class CellLayoutBoard {
+
+ static final int INFINITE = 99999;
+
+ char[][] mBoard = new char[30][30];
+
+ List<TestBoardWidget> mWidgetsRects = new ArrayList<>();
+ Map<Character, TestBoardWidget> mWidgetsMap = new HashMap<>();
+
+ List<TestBoardAppIcon> mIconPoints = new ArrayList<>();
+ Map<Character, TestBoardAppIcon> mIconsMap = new HashMap<>();
+
+ Point mMain = new Point();
+
+ CellLayoutBoard() {
+ for (int x = 0; x < mBoard.length; x++) {
+ for (int y = 0; y < mBoard[0].length; y++) {
+ mBoard[x][y] = '-';
+ }
+ }
+ }
+
+ public List<TestBoardWidget> getWidgets() {
+ return mWidgetsRects;
+ }
+
+ public Point getMain() {
+ return mMain;
+ }
+
+ public TestBoardWidget getWidgetRect(char c) {
+ return mWidgetsMap.get(c);
+ }
+
+ public static TestBoardWidget getWidgetRect(int x, int y, Set<Point> used, char[][] board) {
+ char type = board[x][y];
+ Queue<Point> search = new ArrayDeque<Point>();
+ Point current = new Point(x, y);
+ search.add(current);
+ used.add(current);
+ List<Point> neighbors = new ArrayList<>(List.of(
+ new Point(-1, 0),
+ new Point(0, -1),
+ new Point(1, 0),
+ new Point(0, 1))
+ );
+ Rect widgetRect = new Rect(INFINITE, -INFINITE, -INFINITE, INFINITE);
+ while (!search.isEmpty()) {
+ current = search.poll();
+ widgetRect.top = Math.max(widgetRect.top, current.y);
+ widgetRect.right = Math.max(widgetRect.right, current.x);
+ widgetRect.bottom = Math.min(widgetRect.bottom, current.y);
+ widgetRect.left = Math.min(widgetRect.left, current.x);
+ for (Point p : neighbors) {
+ Point next = new Point(current.x + p.x, current.y + p.y);
+ if (next.x < 0 || next.x >= board.length) continue;
+ if (next.y < 0 || next.y >= board[0].length) continue;
+ if (board[next.x][next.y] == type && !used.contains(next)) {
+ used.add(next);
+ search.add(next);
+ }
+ }
+ }
+ return new TestBoardWidget(type, widgetRect);
+ }
+
+ public static boolean isWidget(char type) {
+ return type != 'i' && type != '-';
+ }
+
+ public static boolean isIcon(char type) {
+ return type == 'i';
+ }
+
+ private static List<TestBoardWidget> getRects(char[][] board) {
+ Set<Point> used = new HashSet<>();
+ List<TestBoardWidget> widgetsRects = new ArrayList<>();
+ for (int x = 0; x < board.length; x++) {
+ for (int y = 0; y < board[0].length; y++) {
+ if (!used.contains(new Point(x, y)) && isWidget(board[x][y])) {
+ widgetsRects.add(getWidgetRect(x, y, used, board));
+ }
+ }
+ }
+ return widgetsRects;
+ }
+
+ private static List<TestBoardAppIcon> getIconPoints(char[][] board) {
+ List<TestBoardAppIcon> iconPoints = new ArrayList<>();
+ for (int x = 0; x < board.length; x++) {
+ for (int y = 0; y < board[0].length; y++) {
+ if (isIcon(board[x][y])) {
+ iconPoints.add(new TestBoardAppIcon(new Point(x, y), board[x][y]));
+ }
+ }
+ }
+ return iconPoints;
+ }
+
+ public static CellLayoutBoard boardFromString(String boardStr) {
+ String[] lines = boardStr.split("\n");
+ CellLayoutBoard board = new CellLayoutBoard();
+
+ for (int y = 0; y < lines.length; y++) {
+ String line = lines[y];
+ for (int x = 0; x < line.length(); x++) {
+ char c = line.charAt(x);
+ if (c == 'm') {
+ board.mMain = new Point(x, y);
+ }
+ if (c != '-') {
+ board.mBoard[x][y] = line.charAt(x);
+ }
+ }
+ }
+ board.mWidgetsRects = getRects(board.mBoard);
+ board.mWidgetsRects.forEach(
+ widgetRect -> board.mWidgetsMap.put(widgetRect.mType, widgetRect));
+ board.mIconPoints = getIconPoints(board.mBoard);
+ return board;
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
new file mode 100644
index 0000000..ed0b71d
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
+
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.celllayout.testcases.FullReorderCase;
+import com.android.launcher3.celllayout.testcases.MoveOutReorderCase;
+import com.android.launcher3.celllayout.testcases.PushReorderCase;
+import com.android.launcher3.celllayout.testcases.ReorderTestCase;
+import com.android.launcher3.celllayout.testcases.SimpleReorderCase;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ReorderWidgets extends AbstractLauncherUiTest {
+
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+
+ private static final String TAG = ReorderWidgets.class.getSimpleName();
+
+ private View getViewAt(int cellX, int cellY) {
+ return getFromLauncher(l -> l.getWorkspace().getScreenWithId(
+ l.getWorkspace().getScreenIdForPageIndex(0)).getChildAt(cellX, cellY));
+ }
+
+ private Point getCellDimensions() {
+ return getFromLauncher(l -> {
+ CellLayout cellLayout = l.getWorkspace().getScreenWithId(
+ l.getWorkspace().getScreenIdForPageIndex(0));
+ return new Point(cellLayout.getWidth() / cellLayout.getCountX(),
+ cellLayout.getHeight() / cellLayout.getCountY());
+ });
+ }
+
+ @Before
+ public void setup() throws Throwable {
+ TaplTestsLauncher3.initialize(this);
+ clearHomescreen();
+ }
+
+ /**
+ * Validate if the given board represent the current CellLayout
+ **/
+ private boolean validateBoard(CellLayoutBoard board) {
+ boolean match = true;
+ Point cellDimensions = getCellDimensions();
+ for (TestBoardWidget widgetRect: board.getWidgets()) {
+ if (widgetRect.shouldIgnore()) {
+ continue;
+ }
+ View widget = getViewAt(widgetRect.getCellX(), widgetRect.getCellY());
+ match &= widgetRect.getSpanX()
+ == Math.round(widget.getWidth() / (float) cellDimensions.x);
+ match &= widgetRect.getSpanY()
+ == Math.round(widget.getHeight() / (float) cellDimensions.y);
+ if (!match) return match;
+ }
+ return match;
+ }
+
+ /**
+ * Fills the given rect in WidgetRect with 1x1 widgets. This is useful to equalize cases.
+ */
+ private void fillWithWidgets(TestBoardWidget widgetRect) {
+ int initX = widgetRect.getCellX();
+ int initY = widgetRect.getCellY();
+ for (int x = 0; x < widgetRect.getSpanX(); x++) {
+ for (int y = 0; y < widgetRect.getSpanY(); y++) {
+ int auxX = initX + x;
+ int auxY = initY + y;
+ try {
+ // this widgets are filling, we don't care if we can't place them
+ addWidgetInCell(
+ new TestBoardWidget('x',
+ new Rect(auxX, auxY, auxX, auxY))
+ );
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to place filling widget at " + auxX + "," + auxY);
+ }
+ }
+ }
+ }
+
+ private void addWidgetInCell(TestBoardWidget widgetRect) {
+ LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+ LauncherAppWidgetInfo item = createWidgetInfo(info,
+ ApplicationProvider.getApplicationContext(), true);
+ item.cellX = widgetRect.getCellX();
+ item.cellY = widgetRect.getCellY();
+
+ item.spanX = widgetRect.getSpanX();
+ item.spanY = widgetRect.getSpanY();
+ addItemToScreen(item);
+ }
+
+ private void addCorrespondingWidgetRect(TestBoardWidget widgetRect) {
+ if (widgetRect.mType == 'x') {
+ fillWithWidgets(widgetRect);
+ } else {
+ addWidgetInCell(widgetRect);
+ }
+ }
+
+ private void runTestCase(ReorderTestCase testCase) {
+ Point mainWidgetCellPos = testCase.mStart.getMain();
+
+ testCase.mStart.getWidgets().forEach(this::addCorrespondingWidgetRect);
+
+ mLauncher.getWorkspace()
+ .getWidgetAtCell(mainWidgetCellPos.x, mainWidgetCellPos.y)
+ .dragWidgetToWorkspace(testCase.moveMainTo.x, testCase.moveMainTo.y)
+ .dismiss(); // dismiss resize frame
+
+ boolean isValid = false;
+ for (CellLayoutBoard board : testCase.mEnd) {
+ isValid |= validateBoard(board);
+ }
+ assertTrue("None of the valid boards match with the current state", isValid);
+ }
+
+ /**
+ * Run only the test define for the current grid size if such test exist
+ *
+ * @param testCaseMap map containing all the tests per grid size (Point)
+ */
+ private void runTestCaseMap(Map<Point, ReorderTestCase> testCaseMap, String testName) {
+ Point iconGridDimensions = mLauncher.getWorkspace().getIconGridDimensions();
+ Log.d(TAG, "Running test " + testName + " for grid " + iconGridDimensions);
+ Assume.assumeTrue(
+ "The test " + testName + " doesn't support " + iconGridDimensions + " grid layout",
+ testCaseMap.containsKey(iconGridDimensions));
+ runTestCase(testCaseMap.get(iconGridDimensions));
+ }
+
+ @Test
+ public void simpleReorder() {
+ runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
+ SimpleReorderCase.class.getSimpleName());
+ }
+
+ @Test
+ public void pushTest() {
+ runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
+ }
+
+ @Test
+ public void fullReorder() {
+ runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
+ }
+
+ @Test
+ public void moveOutReorder() {
+ runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,
+ MoveOutReorderCase.class.getSimpleName());
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java
new file mode 100644
index 0000000..04604d7
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/TestBoardAppIcon.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.graphics.Point;
+
+public class TestBoardAppIcon {
+ public Point coord;
+ public char mType;
+
+ public TestBoardAppIcon(Point coord, char type) {
+ this.coord = coord;
+ mType = type;
+ }
+
+ public char getType() {
+ return mType;
+ }
+
+ public void setType(char type) {
+ mType = type;
+ }
+
+ public Point getCoord() {
+ return coord;
+ }
+
+ public void setCoord(Point coord) {
+ this.coord = coord;
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java
new file mode 100644
index 0000000..7f9aa95
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/TestBoardWidget.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.graphics.Rect;
+
+public class TestBoardWidget {
+ public char mType;
+ public Rect mBounds;
+
+ TestBoardWidget(char type, Rect bounds) {
+ this.mType = type;
+ this.mBounds = bounds;
+ }
+
+ int getSpanX() {
+ return mBounds.right - mBounds.left + 1;
+ }
+
+ int getSpanY() {
+ return mBounds.top - mBounds.bottom + 1;
+ }
+
+ int getCellX() {
+ return mBounds.left;
+ }
+
+ int getCellY() {
+ return mBounds.bottom;
+ }
+
+ boolean shouldIgnore() {
+ return this.mType == 'x';
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
new file mode 100644
index 0000000..ff03a50
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testcases/FullReorderCase.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout.testcases;
+
+import android.graphics.Point;
+
+import java.util.Map;
+
+public class FullReorderCase {
+ private static final String START_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "222mm\n"
+ + "222mm\n"
+ + "ad111\n"
+ + "bc111";
+
+ private static final Point MOVE_TO_5x5 = new Point(0, 4);
+
+ private static final String END_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "222ad\n"
+ + "222bc\n"
+ + "mm111\n"
+ + "mm111";
+
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
+ END_BOARD_STR_5x5);
+
+ private static final String START_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "2222mm\n"
+ + "2222mm\n"
+ + "ad1111\n"
+ + "bc1111";
+
+ private static final Point MOVE_TO_6x5 = new Point(0, 4);
+
+ private static final String END_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "2222ad\n"
+ + "2222bc\n"
+ + "mm1111\n"
+ + "mm1111";
+
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
+ END_BOARD_STR_6x5);
+
+ public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
+ Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
new file mode 100644
index 0000000..32bf05a
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testcases/MoveOutReorderCase.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout.testcases;
+
+import android.graphics.Point;
+
+import java.util.Map;
+
+public class MoveOutReorderCase {
+ private static final String START_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "34-m-\n"
+ + "35111\n"
+ + "32111\n"
+ + "32111";
+
+ private static final Point MOVE_TO_5x5 = new Point(1, 2);
+
+ private static final String END_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "345--\n"
+ + "3m111\n"
+ + "32111\n"
+ + "32111";
+
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_STR_5x5,
+ MOVE_TO_5x5,
+ END_BOARD_STR_5x5);
+
+
+ private static final String START_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "34-m--\n"
+ + "351111\n"
+ + "321111\n"
+ + "321111";
+
+ private static final Point MOVE_TO_6x5 = new Point(1, 2);
+
+ private static final String END_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "345---\n"
+ + "3m1111\n"
+ + "321111\n"
+ + "321111";
+
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_STR_6x5,
+ MOVE_TO_6x5,
+ END_BOARD_STR_6x5);
+
+ public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
+ Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
new file mode 100644
index 0000000..b386946
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testcases/PushReorderCase.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout.testcases;
+
+import android.graphics.Point;
+
+import com.android.launcher3.celllayout.CellLayoutBoard;
+
+import java.util.Map;
+
+public class PushReorderCase {
+ private static final String START_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "222m-\n"
+ + "--111\n"
+ + "--333\n"
+ + "-----";
+ private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ START_BOARD_STR_5x5);
+
+ private static final Point MOVE_TO_5x5 = new Point(2, 1);
+
+ private static final String END_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "--m--\n"
+ + "222--\n"
+ + "--111\n"
+ + "--333";
+ private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ END_BOARD_STR_5x5);
+
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
+ MOVE_TO_5x5,
+ END_BOARD_5x5);
+
+
+ private static final String START_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "2222m-\n"
+ + "--111-\n"
+ + "--333-\n"
+ + "------";
+ private static final CellLayoutBoard START_BOARD_6x5 = CellLayoutBoard.boardFromString(
+ START_BOARD_STR_6x5);
+
+ private static final Point MOVE_TO_6x5 = new Point(2, 1);
+
+ private static final String END_BOARD_STR_6x5 = ""
+ + "xxxxxx\n"
+ + "--m---\n"
+ + "2222--\n"
+ + "--111-\n"
+ + "--333-";
+ private static final CellLayoutBoard END_BOARD_6x5 = CellLayoutBoard.boardFromString(
+ END_BOARD_STR_6x5);
+
+ private static final ReorderTestCase TEST_CASE_6x5 = new ReorderTestCase(START_BOARD_6x5,
+ MOVE_TO_6x5,
+ END_BOARD_6x5);
+
+ public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
+ Map.of(new Point(5, 5), TEST_CASE_5x5, new Point(6, 5), TEST_CASE_6x5);
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java
new file mode 100644
index 0000000..0a28668
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testcases/ReorderTestCase.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout.testcases;
+
+import android.graphics.Point;
+
+import com.android.launcher3.celllayout.CellLayoutBoard;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class ReorderTestCase {
+ public CellLayoutBoard mStart;
+ public Point moveMainTo;
+ public List<CellLayoutBoard> mEnd;
+
+ ReorderTestCase(CellLayoutBoard start, Point moveMainTo, CellLayoutBoard ... end) {
+ mStart = start;
+ this.moveMainTo = moveMainTo;
+ mEnd = Arrays.asList(end);
+ }
+
+ ReorderTestCase(String start, Point moveMainTo, String ... end) {
+ mStart = CellLayoutBoard.boardFromString(start);
+ this.moveMainTo = moveMainTo;
+ mEnd = Arrays
+ .asList(end)
+ .stream()
+ .map(CellLayoutBoard::boardFromString)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
new file mode 100644
index 0000000..57e1398
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/testcases/SimpleReorderCase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout.testcases;
+
+import android.graphics.Point;
+
+import com.android.launcher3.celllayout.CellLayoutBoard;
+
+import java.util.Map;
+
+public class SimpleReorderCase {
+ private static final String START_BOARD_STR = ""
+ + "xxxxx\n"
+ + "--mm-\n"
+ + "--mm-\n"
+ + "-----\n"
+ + "-----";
+ private static final CellLayoutBoard START_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ START_BOARD_STR);
+
+ private static final Point MOVE_TO_5x5 = new Point(4, 4);
+
+ private static final String END_BOARD_STR_5x5 = ""
+ + "xxxxx\n"
+ + "-----\n"
+ + "-----\n"
+ + "---mm\n"
+ + "---mm";
+ private static final CellLayoutBoard END_BOARD_5x5 = CellLayoutBoard.boardFromString(
+ END_BOARD_STR_5x5);
+
+ private static final ReorderTestCase TEST_CASE_5x5 = new ReorderTestCase(START_BOARD_5x5,
+ MOVE_TO_5x5,
+ END_BOARD_5x5);
+
+ public static final Map<Point, ReorderTestCase> TEST_BY_GRID_SIZE =
+ Map.of(new Point(5, 5), TEST_CASE_5x5);
+}
diff --git a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java
index 31468c5..e2ed65f 100644
--- a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java
+++ b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java
@@ -47,8 +47,7 @@
* By changing the WindowManagerProxy we can override the window insets information
**/
private IWindowManager changeWindowManagerInstance(DeviceEmulationData deviceData) {
- WindowManagerProxy.INSTANCE.initializeForTesting(
- new TestWindowManagerProxy(mContext, deviceData));
+ WindowManagerProxy.INSTANCE.initializeForTesting(new TestWindowManagerProxy(deviceData));
return WindowManagerGlobal.getWindowManagerService();
}
@@ -57,8 +56,7 @@
WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext);
// Set up emulation
final int userId = UserHandle.myUserId();
- WindowManagerProxy.INSTANCE.initializeForTesting(
- new TestWindowManagerProxy(mContext, device));
+ WindowManagerProxy.INSTANCE.initializeForTesting(new TestWindowManagerProxy(device));
IWindowManager wm = changeWindowManagerInstance(device);
// Change density twice to force display controller to reset its state
wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density / 2, userId);
diff --git a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java
index cbea688..2d6bbcc 100644
--- a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java
+++ b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java
@@ -19,7 +19,6 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
-import android.view.Display;
import android.view.WindowInsets;
import com.android.launcher3.deviceemulator.models.DeviceEmulationData;
@@ -32,17 +31,12 @@
private final DeviceEmulationData mDevice;
- public TestWindowManagerProxy(Context context, DeviceEmulationData device) {
+ public TestWindowManagerProxy(DeviceEmulationData device) {
super(true);
mDevice = device;
}
@Override
- public boolean isInternalDisplay(Display display) {
- return display.getDisplayId() == Display.DEFAULT_DISPLAY;
- }
-
- @Override
protected int getDimenByName(Resources res, String resName) {
Integer mock = mDevice.resourceOverrides.get(resName);
return mock != null ? mock : super.getDimenByName(res, resName);
@@ -54,27 +48,25 @@
}
@Override
- public CachedDisplayInfo getDisplayInfo(Context context, Display display) {
- int rotation = display.getRotation();
+ public CachedDisplayInfo getDisplayInfo(Context displayInfoContext) {
+ int rotation = getRotation(displayInfoContext);
Point size = new Point(mDevice.width, mDevice.height);
RotationUtils.rotateSize(size, rotation);
Rect cutout = new Rect(mDevice.cutout);
RotationUtils.rotateRect(cutout, rotation);
- return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutout);
+ return new CachedDisplayInfo(size, rotation, cutout);
}
@Override
- public WindowBounds getRealBounds(Context windowContext, Display display,
- CachedDisplayInfo info) {
- return estimateInternalDisplayBounds(windowContext)
- .get(getDisplayId(display)).second[display.getRotation()];
+ public WindowBounds getRealBounds(Context displayInfoContext, CachedDisplayInfo info) {
+ return estimateInternalDisplayBounds(displayInfoContext).get(
+ getDisplayInfo(displayInfoContext))[getDisplay(displayInfoContext).getRotation()];
}
@Override
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
Rect outInsets) {
- outInsets.set(getRealBounds(context, context.getDisplay(),
- getDisplayInfo(context, context.getDisplay())).insets);
+ outInsets.set(getRealBounds(context, getDisplayInfo(context)).insets);
return oldInsets;
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 6f8b9d2..1f6e1ec 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -563,10 +563,13 @@
break;
}
case OVERVIEW: {
- checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
- isResumed);
- assertTrue(TestProtocol.stateOrdinalToString(ordinal),
- ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
+ verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
+ ordinal, TestProtocol.OVERVIEW_STATE_ORDINAL);
+ break;
+ }
+ case SPLIT_SCREEN_SELECT: {
+ verifyOverviewState(launcher, expectedContainerType, isStarted, isResumed,
+ ordinal, TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL);
break;
}
case TASKBAR_ALL_APPS:
@@ -632,5 +635,9 @@
return homeAppIcon;
}
-
+ private void verifyOverviewState(Launcher launcher, ContainerType expectedContainerType,
+ boolean isStarted, boolean isResumed, int ordinal, int expectedOrdinal) {
+ checkLauncherStateInOverview(launcher, expectedContainerType, isStarted, isResumed);
+ assertEquals(TestProtocol.stateOrdinalToString(ordinal), ordinal, expectedOrdinal);
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 8a97c6b..03bf4af 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.content.Intent;
import android.graphics.Point;
@@ -53,7 +54,7 @@
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
import org.junit.Before;
-import org.junit.Rule;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -192,6 +193,17 @@
}
@Test
+ @PortraitLandscape
+ public void testAllAppsDeadzoneForTablet() throws Exception {
+ assumeTrue(mLauncher.isTablet());
+
+ mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+ true /* tapRight */);
+ mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+ false /* tapRight */);
+ }
+
+ @Test
@ScreenRecord // b/202433017
public void testWorkspace() throws Exception {
final Workspace workspace = mLauncher.getWorkspace();
@@ -199,8 +211,8 @@
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
isWorkspaceScrollable(launcher)));
- assertNull("Chrome app was found on empty workspace",
- workspace.tryGetWorkspaceAppIcon("Chrome"));
+ workspace.verifyWorkspaceAppIconIsGone(
+ "Chrome app was found on empty workspace", "Chrome");
workspace.ensureWorkspaceIsScrollable();
@@ -376,6 +388,8 @@
@Test
@PortraitLandscape
+ @ScreenRecord
+ @Ignore // b/233075289
public void testDragToFolder() {
// TODO: add the use case to drag an icon to an existing folder. Currently it either fails
// on tablets or phones due to difference in resolution.
@@ -388,10 +402,10 @@
folder.getAppIcon(GMAIL_APP_NAME);
Workspace workspace = folder.close();
- assertNull(STORE_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME));
- assertNull(GMAIL_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME));
+ workspace.verifyWorkspaceAppIconIsGone(STORE_APP_NAME + " should be moved to a folder.",
+ STORE_APP_NAME);
+ workspace.verifyWorkspaceAppIconIsGone(GMAIL_APP_NAME + " should be moved to a folder.",
+ GMAIL_APP_NAME);
final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
folderIcon = mapIcon.dragToIcon(folderIcon);
@@ -399,8 +413,8 @@
folder.getAppIcon(MAPS_APP_NAME);
workspace = folder.close();
- assertNull(MAPS_APP_NAME + " should be moved to a folder.",
- workspace.tryGetWorkspaceAppIcon(MAPS_APP_NAME));
+ workspace.verifyWorkspaceAppIconIsGone(MAPS_APP_NAME + " should be moved to a folder.",
+ MAPS_APP_NAME);
}
@Test
@@ -424,8 +438,9 @@
for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(appName);
Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(homeAppIcon);
- assertNull(appName + " app was found after being deleted from workspace",
- workspace.tryGetWorkspaceAppIcon(appName));
+ workspace.verifyWorkspaceAppIconIsGone(
+ appName + " app was found after being deleted from workspace",
+ appName);
}
}
@@ -509,10 +524,8 @@
.containsAtLeast(DUMMY_APP_NAME, MAPS_APP_NAME, STORE_APP_NAME);
mLauncher.getWorkspace().getWorkspaceAppIcon(DUMMY_APP_NAME).uninstall();
-
- assertNull(
- DUMMY_APP_NAME + " app was found after being uninstalled",
- mLauncher.getWorkspace().tryGetWorkspaceAppIcon(DUMMY_APP_NAME));
+ mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
+ DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
Map<String, Point> finalPositions =
mLauncher.getWorkspace().getWorkspaceIconsPositions();
@@ -522,6 +535,37 @@
}
}
+ @Test
+ @PortraitLandscape
+ public void testDragShortcutToWorkspaceCell() throws Exception {
+ Point[] targets = getCornersAndCenterPositions();
+
+ for (Point target : targets) {
+ final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ allApps.freeze();
+ try {
+ allApps.getAppIcon(APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem(0)
+ .dragToWorkspace(target.x, target.y);
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testAddDeleteShortcutOnHotseat() {
+ mLauncher.getWorkspace()
+ .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+ .switchToAllApps()
+ .getAppIcon(APP_NAME)
+ .dragToHotseat(0);
+ mLauncher.getWorkspace().deleteAppIcon(
+ mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
+ }
+
/**
* @return List of workspace grid coordinates. Those are not pixels. See {@link
* Workspace#getIconGridDimensions()}
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 35b4ca6..a7a17b1 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -30,7 +30,6 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.allapps.WorkAdapterProvider;
import com.android.launcher3.allapps.WorkEduCard;
import com.android.launcher3.allapps.WorkPausedCard;
import com.android.launcher3.allapps.WorkProfileManager;
@@ -155,7 +154,7 @@
public void testEdu() {
waitForWorkTabSetup();
executeOnLauncher(l -> {
- l.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 0).commit();
+ l.getSharedPrefs().edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP, 0).commit();
((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE);
l.getAppsView().getWorkManager().reset();
});
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 4c41d7e..0a0dfcb 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -32,7 +32,6 @@
public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
mDevice = device;
mLauncher = launcher;
- Log.d("b/196820244", "FailureWatcher.ctor", new Exception());
}
@Override
@@ -48,10 +47,8 @@
public void evaluate() throws Throwable {
boolean success = false;
try {
- Log.d("b/196820244", "Before evaluate");
mDevice.executeShellCommand("cmd statusbar tracing start");
FailureWatcher.super.apply(base, description).evaluate();
- Log.d("b/196820244", "After evaluate");
success = true;
} finally {
// Save artifact for Launcher Winscope trace.
@@ -96,9 +93,7 @@
public static void onError(LauncherInstrumentation launcher, Description description,
Throwable e) {
final UiDevice device = launcher.getDevice();
- Log.d("b/196820244", "onError 1");
if (device == null) return;
- Log.d("b/196820244", "onError 2");
final File sceenshot = diagFile(description, "TestScreenshot", "png");
final File hierarchy = diagFile(description, "Hierarchy", "zip");
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index b7bca02..6a11336 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
@@ -168,6 +169,27 @@
return new OverviewTask(mLauncher, widestTask, this);
}
+ /** Returns an overview task matching TestActivity {@param activityNumber}. */
+ @NonNull
+ public OverviewTask getTestActivityTask(int activityNumber) {
+ final List<UiObject2> taskViews = getTasks();
+ mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
+
+ final String activityName = "TestActivity" + activityNumber;
+ UiObject2 task = null;
+ for (UiObject2 taskView : taskViews) {
+ // TODO(b/239452415): Use equals instead of descEndsWith
+ if (taskView.getParent().hasObject(By.descEndsWith(activityName))) {
+ task = taskView;
+ break;
+ }
+ }
+ mLauncher.assertNotNull(
+ "Unable to find a task with " + activityName + " from the task list", task);
+
+ return new OverviewTask(mLauncher, task, this);
+ }
+
/**
* Returns a list of all tasks fully visible in the tablet grid overview.
*/
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
index 26f0a8b..1352cc0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Folder.java
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -16,11 +16,6 @@
package com.android.launcher3.tapl;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.view.MotionEvent;
-
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
@@ -50,25 +45,15 @@
}
}
- private void touchOutsideFolder() {
- Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer);
- final long downTime = SystemClock.uptimeMillis();
- Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
- containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP,
- containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
- }
-
/**
- * CLose opened folder if possible. It throws assertion error if the folder is already closed.
+ * Close opened folder if possible. It throws assertion error if the folder is already closed.
*/
public Workspace close() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"Want to close opened folder")) {
mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
- touchOutsideFolder();
+ mLauncher.touchOutsideContainer(this.mContainer, false /* tapRight */);
mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID);
return mLauncher.getWorkspace();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index c275f3b..7123de4 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -19,6 +19,7 @@
import androidx.test.uiautomator.UiObject2;
public class HomeAllApps extends AllApps {
+ private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
HomeAllApps(LauncherInstrumentation launcher) {
super(launcher);
@@ -45,4 +46,23 @@
protected boolean hasSearchBox() {
return true;
}
+
+ /**
+ * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
+ * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+ */
+ public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap outside AllApps bottom sheet on the "
+ + (tapRight ? "right" : "left"))) {
+ final UiObject2 allAppsBottomSheet =
+ mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+ mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+ try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+ "tapped outside AllApps bottom sheet")) {
+ return mLauncher.getWorkspace();
+ }
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index 71d8ba9..693baa0 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -103,40 +103,45 @@
}
/**
- * Drag an object to the given cell in workspace. The target cell must be empty.
+ * Drag an object to the given cell in hotseat. The target cell should be expected to be empty.
*
- * @param cellX zero based column number, starting from the left of the screen.
- * @param cellY zero based row number, starting from the top of the screen.
+ * @param cellInd zero based index number of the hotseat cells.
+ * @return the workspace app icon.
*/
- public HomeAppIcon dragToWorkspace(int cellX, int cellY) {
+ @NonNull
+ public WorkspaceAppIcon dragToHotseat(int cellInd) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))
+ String.format("want to drag the icon to hotseat cell %d", cellInd))
) {
- final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY);
- Workspace.dragIconToWorkspace(
+ final Supplier<Point> dest = () -> Workspace.getHotseatCellCenter(mLauncher, cellInd);
+
+ Workspace.dragIconToHotseat(
mLauncher,
- /* launchable= */ this,
+ this,
dest,
() -> addExpectedEventsForLongClick(),
/*expectDropEvents= */ null);
try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
WorkspaceAppIcon appIcon =
- (WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName);
+ (WorkspaceAppIcon) mLauncher.getWorkspace().getHotseatAppIcon(mAppName);
mLauncher.assertTrue(
- String.format(
- "The %s icon should be in the cell (%d, %d).", mAppName, cellX,
- cellY),
- appIcon.isInCell(cellX, cellY));
+ String.format("The %s icon should be in the hotseat cell %d.", mAppName,
+ cellInd),
+ appIcon.isInHotseatCell(cellInd));
return appIcon;
}
}
}
-
/** This method requires public access, however should not be called in tests. */
@Override
public Launchable getLaunchable() {
return this;
}
+
+ boolean isInHotseatCell(int cellInd) {
+ final Point center = Workspace.getHotseatCellCenter(mLauncher, cellInd);
+ return mObject.getVisibleBounds().contains(center.x, center.y);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9d25b1b..5f92097 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -84,7 +84,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -101,7 +100,6 @@
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
private static final int GESTURE_STEP_MS = 16;
- private static final long FORCE_PAUSE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2);
static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
@@ -123,8 +121,8 @@
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
- WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP,
- TASKBAR_ALL_APPS
+ WORKSPACE, HOME_ALL_APPS, OVERVIEW, SPLIT_SCREEN_SELECT, WIDGETS, FALLBACK_OVERVIEW,
+ LAUNCHED_APP, TASKBAR_ALL_APPS
}
public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
@@ -171,7 +169,7 @@
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
private static final String TASKBAR_RES_ID = "taskbar_view";
- public static final int WAIT_TIME_MS = 60000;
+ public static final int WAIT_TIME_MS = 30000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -362,10 +360,6 @@
return getRealDisplaySize().x / 2f;
}
- private void setForcePauseTimeout(long timeout) {
- getTestInfo(TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT, Long.toString(timeout));
- }
-
public void setEnableRotation(boolean on) {
getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
}
@@ -379,6 +373,17 @@
sActiveContainer = new WeakReference<>(container);
}
+ /**
+ * Sets the accesibility interactive timeout to be effectively indefinite (UI using this
+ * accesibility timeout will not automatically dismiss if true).
+ */
+ void setIndefiniteAccessibilityInteractiveUiTimeout(boolean indefiniteTimeout) {
+ final String cmd = indefiniteTimeout
+ ? "settings put secure accessibility_interactive_ui_timeout_ms 10000"
+ : "settings delete secure accessibility_interactive_ui_timeout_ms";
+ logShellCommand(cmd);
+ }
+
public NavigationModel getNavigationModel() {
final Context baseContext = mInstrumentation.getTargetContext();
try {
@@ -739,7 +744,8 @@
return waitForLauncherObject(APPS_RES_ID);
}
- case OVERVIEW: {
+ case OVERVIEW:
+ case SPLIT_SCREEN_SELECT: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
@@ -886,7 +892,6 @@
final String action;
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
checkForAnomaly(false, true);
- setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
final Point displaySize = getRealDisplaySize();
// The swipe up to home gesture starts from inside the launcher when the user is
@@ -1087,6 +1092,14 @@
}
@NonNull
+ UiObject2 waitForSystemUiObject(BySelector selector) {
+ final UiObject2 object = TestHelpers.wait(
+ Until.findObject(selector), WAIT_TIME_MS);
+ assertNotNull("Can't find a systemui object with selector: " + selector, object);
+ return object;
+ }
+
+ @NonNull
UiObject2 waitForNavigationUiObject(String resId) {
String resPackage = getNavigationButtonResPackage();
final UiObject2 object = mDevice.wait(
@@ -1217,6 +1230,13 @@
return object;
}
+ @NonNull
+ List<UiObject2> waitForObjectsBySelector(BySelector selector) {
+ final List<UiObject2> objects = mDevice.wait(Until.findObjects(selector), WAIT_TIME_MS);
+ assertNotNull("Can't find any view in Launcher, selector: " + selector, objects);
+ return objects;
+ }
+
private UiObject2 waitForObjectBySelector(BySelector selector) {
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
@@ -1831,4 +1851,26 @@
return ResourceUtils.getBoolByName(
"config_supportsRoundedCornersOnWindows", resources, false);
}
+
+ /**
+ * Taps outside container to dismiss.
+ * @param container container to be dismissed
+ * @param tapRight tap on the right of the container if true, or left otherwise
+ */
+ void touchOutsideContainer(UiObject2 container, boolean tapRight) {
+ try (LauncherInstrumentation.Closable c = addContextLayer(
+ "want to tap outside container on the " + (tapRight ? "right" : "left"))) {
+ Rect containerBounds = getVisibleBounds(container);
+ final long downTime = SystemClock.uptimeMillis();
+ final Point tapTarget = new Point(
+ tapRight
+ ? (containerBounds.right + getRealDisplaySize().x) / 2
+ : containerBounds.left / 2,
+ containerBounds.top + 1);
+ sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 66a51a5..50c2136 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -21,7 +21,7 @@
/**
* Overview pane.
*/
-public final class Overview extends BaseOverview {
+public class Overview extends BaseOverview {
Overview(LauncherInstrumentation launcher) {
super(launcher);
@@ -29,7 +29,7 @@
@Override
protected ContainerType getContainerType() {
- return LauncherInstrumentation.ContainerType.OVERVIEW;
+ return ContainerType.OVERVIEW;
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 2f44bb6..5710713 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -41,6 +41,8 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to click screenshot button and exit screenshot ui")) {
+ mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(true);
+
UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions,
"action_screenshot");
@@ -62,6 +64,8 @@
return new Overview(mLauncher);
}
}
+ } finally {
+ mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(false);
}
}
@@ -82,7 +86,6 @@
"clicked select button")) {
return getSelectModeButtons();
}
-
}
}
@@ -99,4 +102,22 @@
return new SelectModeButtons(selectModeButtons, mLauncher);
}
}
+
+ /**
+ * Clicks split button and enters split select mode.
+ */
+ @NonNull
+ public SplitScreenSelect clickSplit() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click split button to enter split select mode")) {
+ UiObject2 split = mLauncher.waitForObjectInContainer(mOverviewActions,
+ "action_split");
+ mLauncher.clickLauncherObject(split);
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "clicked split")) {
+ return new SplitScreenSelect(mLauncher);
+ }
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index c8caa42..ac7424e 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -20,6 +20,8 @@
import android.graphics.Rect;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
@@ -32,8 +34,12 @@
* A recent task in the overview panel carousel.
*/
public final class OverviewTask {
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
static final Pattern TASK_START_EVENT =
Pattern.compile("startActivityFromRecentsAsync");
+ static final Pattern SPLIT_START_EVENT =
+ Pattern.compile("launchSplitTasks");
private final LauncherInstrumentation mLauncher;
private final UiObject2 mTask;
private final BaseOverview mOverview;
@@ -125,7 +131,7 @@
}
/**
- * Clicks at the task.
+ * Clicks the task.
*/
public LaunchedAppState open() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
@@ -136,8 +142,21 @@
() -> "Launching task didn't open a new window: "
+ mTask.getParent().getContentDescription(),
"clicking an overview task");
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
- return new LaunchedAppState(mLauncher);
+ if (mOverview.getContainerType()
+ == LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SPLIT_START_EVENT);
+
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "launched splitscreen")) {
+
+ BySelector divider = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+ mLauncher.waitForSystemUiObject(divider);
+ return new LaunchedAppState(mLauncher);
+ }
+ } else {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+ return new LaunchedAppState(mLauncher);
+ }
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 82652c7..ddeeac2 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -26,6 +26,7 @@
public class SearchResultFromQsb {
// The input resource id in the search box.
private static final String INPUT_RES = "input";
+ private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
private final LauncherInstrumentation mLauncher;
SearchResultFromQsb(LauncherInstrumentation launcher) {
@@ -47,4 +48,23 @@
UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
return new AllAppsAppIcon(mLauncher, icon);
}
+
+ /**
+ * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
+ * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+ */
+ public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to tap outside AllApps bottom sheet on the "
+ + (tapRight ? "right" : "left"))) {
+ final UiObject2 allAppsBottomSheet =
+ mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+ mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+ try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+ "tapped outside AllApps bottom sheet")) {
+ return mLauncher.getWorkspace();
+ }
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
new file mode 100644
index 0000000..3cf3ed6
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitScreenSelect.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
+
+/**
+ * Represents a special state in Overview where the initial split app is shoved to the side and a
+ * second split app can be selected.
+ */
+public class SplitScreenSelect extends Overview {
+
+ SplitScreenSelect(LauncherInstrumentation launcher) {
+ super(launcher);
+ }
+
+ @Override
+ protected ContainerType getContainerType() {
+ return ContainerType.SPLIT_SCREEN_SELECT;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 2346249..e1a09af 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -69,7 +69,24 @@
*/
@NonNull
public WidgetResizeFrame dragWidgetToWorkspace() {
- return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false);
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false, -1,
+ -1);
+ }
+ }
+
+ /**
+ * Drags a non-configurable widget from the widgets container to the workspace at cellX and
+ * cellY and returns the resize frame that is shown after the widget is added.
+ */
+ @NonNull
+ public WidgetResizeFrame dragWidgetToWorkspace(int cellX, int cellY) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "Dragging widget to workspace cell " + cellX + "," + cellY)) {
+ return dragWidgetToWorkspace(/* configurable= */ false, /* acceptsConfig= */ false,
+ cellX, cellY);
+ }
}
/**
@@ -79,7 +96,32 @@
*/
@Nullable
public WidgetResizeFrame dragConfigWidgetToWorkspace(boolean acceptsConfig) {
- return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig);
+ // TODO(b/239438337, fransebas) add correct event checking for this case
+ //try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ return dragWidgetToWorkspace(/* configurable= */ true, acceptsConfig, -1, -1);
+ //}
+ }
+
+ /**
+ * Drags an object to the center of homescreen.
+ *
+ * @param startsActivity whether it's expected to start an activity.
+ * @param isWidgetShortcut whether we drag a widget shortcut
+ * @param cellX X position in the CellLayout
+ * @param cellY Y position in the CellLayout
+ */
+ private void dragToWorkspace(boolean startsActivity, boolean isWidgetShortcut, int cellX,
+ int cellY) {
+ Launchable launchable = getLaunchable();
+ LauncherInstrumentation launcher = launchable.mLauncher;
+ Workspace.dragIconToWorkspace(
+ launcher,
+ launchable,
+ () -> Workspace.getCellCenter(launchable.mLauncher, cellX, cellY),
+ startsActivity,
+ isWidgetShortcut,
+ launchable::addExpectedEventsForLongClick);
+
}
/**
@@ -88,11 +130,28 @@
*
* <p> If {@code configurable} is true, then either accepts or cancels the configuration based
* on {@code acceptsConfig}.
+ * <p> If either {@code cellX} or {@code cellY} are negative, then a default location would be
+ * chosen
+ *
+ * @param configurable if the widget has a configuration activity.
+ * @param acceptsConfig if the widget has a configuration, then if we should accept it or
+ * cancel it
+ * @param cellX X position to drop the widget in the workspace
+ * @param cellY Y position to drop the widget in the workspace
+ * @return returns the given resize frame of the widget after being dropped, if
+ * configurable is true and acceptsConfig is false then the widget would not be places and will
+ * be cancel and it returns null.
*/
@Nullable
- private WidgetResizeFrame dragWidgetToWorkspace(
- boolean configurable, boolean acceptsConfig) {
- dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false);
+ private WidgetResizeFrame dragWidgetToWorkspace(boolean configurable, boolean acceptsConfig,
+ int cellX, int cellY) {
+ if (cellX == -1 || cellY == -1) {
+ internalDragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */
+ false);
+ } else {
+ dragToWorkspace(/* startsActivity= */ configurable, /* isWidgetShortcut= */ false,
+ cellX, cellY);
+ }
if (configurable) {
// Configure the widget.
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index eb7f05b..6044b06 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -39,6 +39,7 @@
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.testing.HotseatCellCenterRequest;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.testing.WorkspaceCellCenterRequest;
@@ -89,7 +90,7 @@
final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
final int swipeHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT)
+ TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
LauncherInstrumentation.log(
"switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
@@ -140,6 +141,20 @@
}
}
+ /**
+ * Waits for an app icon to be gone (e.g. after uninstall). Fails if it remains.
+ *
+ * @param errorMessage error message thrown then the icon doesn't disappear.
+ * @param appName app that should be gone.
+ */
+ public void verifyWorkspaceAppIconIsGone(String errorMessage, String appName) {
+ final UiObject2 workspace = verifyActiveContainer();
+ assertTrue(errorMessage,
+ workspace.wait(
+ Until.gone(AppIcon.getAppIconSelector(appName, mLauncher)),
+ LauncherInstrumentation.WAIT_TIME_MS));
+ }
+
/**
* Returns an icon for the app; fails if the icon doesn't exist.
@@ -214,10 +229,11 @@
private void dragIcon(UiObject2 workspace, HomeAppIcon homeAppIcon, int pageDelta) {
int pageWidth = mLauncher.getDevice().getDisplayWidth() / pagesPerScreen();
int targetX = (pageWidth / 2) + pageWidth * pageDelta;
+ int targetY = mLauncher.getVisibleBounds(workspace).centerY();
dragIconToWorkspace(
mLauncher,
homeAppIcon,
- new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
+ () -> new Point(targetX, targetY),
false,
false,
() -> mLauncher.expectEvent(
@@ -236,8 +252,26 @@
}
/**
+ * Returns an icon for the given cell; fails if the icon doesn't exist.
+ *
+ * @param cellInd zero based index number of the hotseat cells.
+ * @return app icon.
+ */
+ @NonNull
+ public HomeAppIcon getHotseatAppIcon(int cellInd) {
+ List<UiObject2> icons = mHotseat.findObjects(AppIcon.getAnyAppIconSelector());
+ final Point center = getHotseatCellCenter(mLauncher, cellInd);
+ return icons.stream()
+ .filter(icon -> icon.getVisibleBounds().contains(center.x, center.y))
+ .findFirst()
+ .map(icon -> new WorkspaceAppIcon(mLauncher, icon))
+ .orElseThrow(() ->
+ new AssertionError("Unable to get a hotseat icon on " + cellInd));
+ }
+
+ /**
* @return map of text -> center of the view. In case of icons with the same name, the one with
- * lower x coordinate is selected.
+ * lower x coordinate is selected.
*/
public Map<String, Point> getWorkspaceIconsPositions() {
final UiObject2 workspace = verifyActiveContainer();
@@ -250,6 +284,7 @@
/* valueMapper= */ UiObject2::getVisibleCenter,
/* mergeFunction= */ (p1, p2) -> p1.x < p2.x ? p1 : p2));
}
+
/*
* Get the center point of the delete/uninstall icon in the drop target bar.
*/
@@ -345,6 +380,11 @@
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ static Point getHotseatCellCenter(LauncherInstrumentation launcher, int cellInd) {
+ return launcher.getTestInfo(HotseatCellCenterRequest.builder()
+ .setCellInd(cellInd).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/**
* Finds folder icons in the current workspace.
*
@@ -372,7 +412,7 @@
}
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
- Point dest, boolean startsActivity, boolean isWidgetShortcut,
+ Supplier<Point> dest, boolean startsActivity, boolean isWidgetShortcut,
Runnable expectLongClickEvents) {
Runnable expectDropEvents = null;
if (startsActivity || isWidgetShortcut) {
@@ -380,7 +420,7 @@
LauncherInstrumentation.EVENT_START);
}
dragIconToWorkspace(
- launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents);
+ launcher, launchable, dest, expectLongClickEvents, expectDropEvents);
}
/**
@@ -456,6 +496,25 @@
() -> "Page scroll didn't happen", "Scrolling page");
}
+ static void dragIconToHotseat(
+ LauncherInstrumentation launcher,
+ Launchable launchable,
+ Supplier<Point> dest,
+ Runnable expectLongClickEvents,
+ @Nullable Runnable expectDropEvents) {
+ final long downTime = SystemClock.uptimeMillis();
+ Point dragStart = launchable.startDrag(
+ downTime,
+ expectLongClickEvents,
+ /* runToSpringLoadedState= */ true);
+ Point targetDest = dest.get();
+
+ launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
+ downTime, SystemClock.uptimeMillis(), false,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+ }
+
/**
* Flings to get to screens on the right. Waits for scrolling and a possible overscroll
* recoil to complete.
@@ -523,6 +582,32 @@
}
}
+ /**
+ * @param cellX X position of the widget trying to get.
+ * @param cellY Y position of the widget trying to get.
+ * @return returns the Widget in the given position in the Launcher or an Exception if no such
+ * widget is in that position.
+ */
+ @NonNull
+ public Widget getWidgetAtCell(int cellX, int cellY) {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "getting widget at cell position " + cellX + "," + cellY)) {
+ final List<UiObject2> widgets = mLauncher.waitForObjectsBySelector(
+ By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView"));
+ Point coordinateInScreen = Workspace.getCellCenter(mLauncher, cellX, cellY);
+ for (UiObject2 widget : widgets) {
+ if (widget.getVisibleBounds().contains(coordinateInScreen.x,
+ coordinateInScreen.y)) {
+ return new Widget(mLauncher, widget);
+ }
+ }
+ }
+ mLauncher.fail("Unable to find widget at cell " + cellX + "," + cellY);
+ // This statement is unreachable because mLauncher.fail throws an exception
+ // but is needed for compiling
+ return null;
+ }
+
@Nullable
public Widget tryGetPendingWidget(long timeout) {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index d8d4420..141476c 100644
--- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -17,6 +17,8 @@
import android.graphics.Point;
+import java.util.function.Supplier;
+
/** Launchable that can serve as a source for dragging and dropping to the workspace. */
interface WorkspaceDragSource {
@@ -30,20 +32,67 @@
Launchable launchable = getLaunchable();
LauncherInstrumentation launcher = launchable.mLauncher;
try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
- final Point launchableCenter = launchable.getObject().getVisibleCenter();
- final Point displaySize = launcher.getRealDisplaySize();
- final int width = displaySize.x / 2;
+ internalDragToWorkspace(startsActivity, isWidgetShortcut);
+ }
+ }
+
+ /**
+ * TODO(Redesign WorkspaceDragSource to have actual private methods)
+ * Temporary private method
+ *
+ * @param startsActivity whether it's expected to start an activity.
+ * @param isWidgetShortcut whether we drag a widget shortcut
+ */
+ default void internalDragToWorkspace(boolean startsActivity, boolean isWidgetShortcut) {
+ Launchable launchable = getLaunchable();
+ LauncherInstrumentation launcher = launchable.mLauncher;
+ final Point launchableCenter = launchable.getObject().getVisibleCenter();
+ final Point displaySize = launcher.getRealDisplaySize();
+ final int width = displaySize.x / 2;
+ Workspace.dragIconToWorkspace(
+ launcher,
+ launchable,
+ () -> new Point(
+ launchableCenter.x >= width
+ ? launchableCenter.x - width / 2
+ : launchableCenter.x + width / 2,
+ displaySize.y / 2),
+ startsActivity,
+ isWidgetShortcut,
+ launchable::addExpectedEventsForLongClick);
+ }
+
+ /**
+ * Drag an object to the given cell in workspace. The target cell must be empty.
+ *
+ * @param cellX zero based column number, starting from the left of the screen.
+ * @param cellY zero based row number, starting from the top of the screen. *
+ */
+ default HomeAppIcon dragToWorkspace(int cellX, int cellY) {
+ Launchable launchable = getLaunchable();
+ final String iconName = launchable.getObject().getText();
+ LauncherInstrumentation launcher = launchable.mLauncher;
+ try (LauncherInstrumentation.Closable e = launcher.eventsCheck();
+ LauncherInstrumentation.Closable c = launcher.addContextLayer(
+ String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))) {
+ final Supplier<Point> dest = () -> Workspace.getCellCenter(launcher, cellX, cellY);
Workspace.dragIconToWorkspace(
launcher,
launchable,
- new Point(
- launchableCenter.x >= width
- ? launchableCenter.x - width / 2
- : launchableCenter.x + width / 2,
- displaySize.y / 2),
- startsActivity,
- isWidgetShortcut,
- launchable::addExpectedEventsForLongClick);
+ dest,
+ launchable::addExpectedEventsForLongClick,
+ /*expectDropEvents= */ null);
+
+ try (LauncherInstrumentation.Closable ignore = launcher.addContextLayer("dragged")) {
+ WorkspaceAppIcon appIcon =
+ (WorkspaceAppIcon) launcher.getWorkspace().getWorkspaceAppIcon(iconName);
+ launcher.assertTrue(
+ String.format(
+ "The %s icon should be in the cell (%d, %d).", iconName, cellX,
+ cellY),
+ appIcon.isInCell(cellX, cellY));
+ return appIcon;
+ }
}
}