Detect force visible nav bar based on kids mode. am: 6d17a71d19 am: ac7f012ba0
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19038728
Change-Id: Ie4c215ae488106822ac8b1959ec58514afc7a0d1
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/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/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index f233bde..732f511 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>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3072a3e..0fd3c4a 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -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>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2239102..95d6dd0 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -92,6 +92,9 @@
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
+import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -343,15 +346,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(),
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index e1a3b72..b20752d 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;
@@ -527,7 +530,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/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..01cf23b 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;
@@ -105,6 +106,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 FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
@@ -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));
@@ -443,6 +448,8 @@
| 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);
@@ -453,6 +460,7 @@
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
@@ -750,6 +758,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..f472427 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.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index dc2e3b6..d1994e7 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,12 +248,20 @@
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,
PixelFormat.TRANSLUCENT);
@@ -468,6 +478,8 @@
fromInit);
mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
+ mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
+ (systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
}
/**
@@ -612,7 +624,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/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/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 6a6a693..e4f82d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -18,6 +18,7 @@
import android.graphics.Insets
import android.graphics.Region
import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
+import android.view.InsetsState
import android.view.WindowManager
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
@@ -61,9 +62,6 @@
)
)
- windowLayoutParams.providedInternalInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
- windowLayoutParams.providedInternalImeInsets = arrayOfNulls<Insets>(ITYPE_SIZE)
-
onTaskbarWindowHeightOrInsetsChanged()
windowLayoutParams.insetsRoundedCornerFrame = true
@@ -75,32 +73,23 @@
}
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)
+ var imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+ for (provider in windowLayoutParams.providedInsets) {
+ provider.imeInsetsSize = imeInsetsSize
+ }
}
/**
@@ -151,13 +140,10 @@
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.println("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
+ + " insetsSize=" + provider.insetsSize
+ + " imeInsetsSize=" + provider.imeInsetsSize + ")")
+ }
}
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index dc0ef27..ff11f67 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,6 +33,7 @@
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.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
@@ -44,7 +47,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 +55,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 +104,11 @@
}
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
if (!mShouldDelayLauncherStateAnim) {
- applyState();
+ if (toState == LauncherState.NORMAL) {
+ applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
+ } else {
+ applyState();
+ }
}
}
@@ -122,7 +131,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 +183,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 +281,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 +322,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 +352,7 @@
.setDuration(duration));
}
+ animatorSet.setInterpolator(EMPHASIZED);
if (start) {
animatorSet.start();
}
@@ -374,6 +397,12 @@
mIconAlignmentForLauncherState.finishAnimation();
animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
.setDuration(duration));
+ if (DEBUG) {
+ Log.d(TAG, "mIconAlignmentForLauncherState - "
+ + mIconAlignmentForLauncherState.value
+ + " -> " + toAlignment + ": " + duration);
+ }
+ animatorSet.setInterpolator(EMPHASIZED);
}
}
@@ -396,17 +425,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 +445,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 +489,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/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6f88d64..6a43cc5 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;
@@ -78,6 +80,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 +121,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 +173,7 @@
if (mAllAppsButton != null) {
removeView(mAllAppsButton);
}
+ removeView(mQsb);
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
@@ -242,6 +250,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,7 +286,12 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int count = getChildCount();
- int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
+ int countExcludingQsb = count;
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ if (deviceProfile.isQsbInline) {
+ countExcludingQsb--;
+ }
+ int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
boolean layoutRtl = isLayoutRtl();
int iconEnd = right - (right - left - spaceNeeded) / 2;
@@ -292,10 +310,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 +400,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..0cbd0d1 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,8 @@
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;
+ private static final int NUM_ALPHA_CHANNELS = 6;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
@@ -210,9 +215,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 +230,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 +253,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 +267,46 @@
// 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);
+
+ int qsbContentHeight = child.getHeight() - child.getPaddingTop()
+ - child.getPaddingBottom();
+ float scale = ((float) taskbarDp.iconSizePx) / qsbContentHeight;
+ 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;
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..61b038e 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;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 76f7718..b3ef71e 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -230,7 +230,6 @@
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 =
@@ -571,7 +570,7 @@
protected void notifyGestureAnimationStartToRecents() {
Task[] runningTasks;
- if (mIsSwipeForStagedSplit) {
+ if (mIsSwipeForSplit) {
int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds);
} else {
@@ -1137,7 +1136,9 @@
mInputConsumerProxy.enable();
}
if (endTarget == HOME) {
- duration = HOME_DURATION;
+ duration = 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 +1281,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 +1394,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
@@ -1675,7 +1676,7 @@
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
- if (mRecentsAnimationTargets != null) {
+ if (mRecentsAnimationTargets != null && wasVisible) {
setDividerShown(true /* shown */, true /* immediate */);
}
@@ -1792,12 +1793,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..52abb92 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -56,11 +56,9 @@
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;
@@ -258,7 +256,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 +268,29 @@
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 (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);
}
}
@@ -341,7 +321,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/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..b297973 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));
}
}
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/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..749c07b 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;
@@ -32,6 +29,8 @@
import android.view.View;
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 +38,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 +55,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 +91,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
@@ -103,7 +118,7 @@
protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
@Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity,
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
TaskIdAttributeContainer taskContainer) {
final Task task = taskContainer.getTask();
if (!task.isDockable) {
@@ -112,8 +127,8 @@
if (!isAvailable(activity, task.key.displayId)) {
return null;
}
- return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this,
- mLauncherEvent);
+ return Collections.singletonList(new MultiWindowSystemShortcut(mIconRes,
+ mTextRes, activity, taskContainer, this, mLauncherEvent));
}
}
@@ -242,34 +257,40 @@
}
}
- /** @Deprecated */
- TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen,
- R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) {
-
+ /**
+ * 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
+ * * 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 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);
- }
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ DeviceProfile deviceProfile = activity.getDeviceProfile();
+ TaskView taskView = taskContainer.getTaskView();
+ 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));
- @Override
- protected ActivityOptions makeLaunchOptions(Activity activity) {
- final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
- activity.getDisplayId());
- if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+ if (taskViewHasMultipleTasks || notEnoughTasksToSplit
+ || (isFocusedTask && isTaskInExpectedScrollPosition)) {
return null;
}
- boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
- return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
- }
- @Override
- protected boolean onActivityStarted(BaseDraggingActivity activity) {
- return true;
+ return orientationHandler.getSplitPositionOptions(deviceProfile)
+ .stream()
+ .map((Function<SplitPositionOption, SystemShortcut>) option ->
+ new SplitSelectSystemShortcut(activity, taskView, option))
+ .collect(Collectors.toList());
}
};
@@ -297,18 +318,22 @@
}
};
- TaskShortcutFactory PIN = (activity, taskContainer) -> {
- if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
- 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));
}
- 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 new PinSystemShortcut(activity, taskContainer);
};
class PinSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
@@ -335,26 +360,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/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 5680170..aa52789 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;
@@ -148,45 +149,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
@@ -284,7 +290,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/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 6038a22..14190b3 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -416,7 +416,7 @@
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) {
@@ -425,12 +425,6 @@
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);
} else {
float factor = scale / (scale - 1);
outPivot.set(taskView.left * factor, taskView.top * factor);
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..833d705 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -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..1acdec1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -40,7 +40,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 +100,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 +152,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 +180,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 +390,8 @@
.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()) {
+ builder.withLayer(mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
}
}
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..cb88068 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -17,7 +17,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 +53,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 +78,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 +126,8 @@
}
}
- public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
- mSplitBoundsConfig = stagedSplitBounds;
+ public void updateSplitBoundsConfig(SplitBounds splitBounds) {
+ mSplitBoundsConfig = splitBounds;
invalidate();
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1c4e497..3133453 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;
@@ -233,10 +231,6 @@
return 0;
}
- if (mDp.isVerticalBarLayout()) {
- return mDp.getInsets().bottom;
- }
-
if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
return mDp.getOverviewActionsClaimedSpaceBelow();
}
@@ -254,12 +248,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..12ddc38 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;
@@ -624,12 +625,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 +1448,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);
}
@@ -2764,11 +2767,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 {
@@ -4091,6 +4098,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 +4110,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 +4179,10 @@
taskViewsFloat.first.set(this, getSplitSelectTranslation());
taskViewsFloat.second.set(this, 0f);
+ if (mSplitInstructionsView != null) {
+ mSplitInstructionsView.ensureProperRotation();
+ }
+
applySplitPrimaryScrollOffset();
}
@@ -4473,7 +4492,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 +4542,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 +4562,7 @@
new PictureInPictureSurfaceTransaction.Builder()
.setAlpha(0f)
.build();
+ tx.setShouldDisableCanAffectSystemUiFlags(false);
int[] taskIds = TopTaskTracker.INSTANCE.get(getContext()).getRunningSplitTaskIds();
for (int taskId : taskIds) {
mRecentsAnimationController.setFinishTaskTransaction(taskId,
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..69cad69 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -451,9 +451,8 @@
// 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) {
@@ -562,13 +561,8 @@
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);
- }
+ mClippedInsets.offsetTo(thumbnailClipHint.left * scale,
+ thumbnailClipHint.top * scale);
mMatrix.setTranslate(
-thumbnailClipHint.left * scale,
-thumbnailClipHint.top * scale);
@@ -587,16 +581,10 @@
}
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.right = Math.max(0,
+ widthWithInsets - mClippedInsets.left - canvasWidth);
+ mClippedInsets.bottom = Math.max(0,
+ heightWithInsets - mClippedInsets.top - canvasHeight);
mMatrix.postScale(thumbnailScale, thumbnailScale);
mIsOrientationChanged = isOrientationDifferent;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d58bb7c..377467f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -19,7 +19,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
-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;
@@ -67,7 +66,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;
@@ -726,10 +724,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) {
@@ -1343,7 +1337,7 @@
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile(), taskContainer)) {
+ taskContainer)) {
info.addAction(s.createAccessibilityAction(context));
}
}
@@ -1381,7 +1375,7 @@
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile(), taskContainer)) {
+ taskContainer)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
return true;
@@ -1537,7 +1531,6 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
}
@@ -1591,10 +1584,9 @@
}
mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
currentInsetsRight, insetsBottom * fullscreenProgress);
- float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
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/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/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/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/attrs.xml b/res/values/attrs.xml
index 13f20c2..9f25905 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" />
@@ -252,9 +254,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 -->
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/dimens.xml b/res/values/dimens.xml
index 8403af4..39f0a2b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -391,8 +391,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>
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/styles.xml b/res/values/styles.xml
index 2109510..65bba7b 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">
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index b276397..8c1f505 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -414,10 +414,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);
@@ -802,13 +799,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 +813,7 @@
allAppsIconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * scale);
allAppsIconDrawablePaddingPx =
res.getDimensionPixelSize(R.dimen.all_apps_icon_drawable_padding);
+ allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
}
updateAllAppsContainerWidth(res);
@@ -1150,7 +1148,7 @@
private int getHotseatBottomPadding() {
if (isQsbInline) {
- return getQsbOffsetY() - (Math.abs(hotseatQsbHeight - hotseatCellHeightPx) / 2);
+ return getQsbOffsetY() + ((hotseatQsbHeight - hotseatCellHeightPx) / 2);
} else {
return (getQsbOffsetY() - taskbarSize) / 2;
}
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..8c4c662 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -206,6 +206,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..dacbe92 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -61,8 +61,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;
@@ -234,7 +232,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,
@@ -633,18 +632,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);
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 808bf96..0c7c311 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;
@@ -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/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/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/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index ecadec6..499e749 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -253,7 +253,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)) {
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index b70cb13..8443923 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -158,10 +158,10 @@
workFabMarginBottom += mAllApps.mActivityContext.getDeviceProfile().getInsets().bottom;
}
lp.bottomMargin = workFabMarginBottom;
- int totalScreenWidth = mDeviceProfile.widthPx;
+ int allAppsContainerWidth = mAllApps.getVisibleContainerView().getWidth();
int personalWorkTabWidth =
mAllApps.mActivityContext.getAppsView().getFloatingHeaderView().getTabWidth();
- lp.rightMargin = lp.leftMargin = (totalScreenWidth - personalWorkTabWidth) / 2;
+ lp.rightMargin = lp.leftMargin = (allAppsContainerWidth - personalWorkTabWidth) / 2;
if (mWorkModeSwitch.getParent() != mAllApps) {
mAllApps.addView(mWorkModeSwitch);
}
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..38b5c65 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");
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/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/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..a7b0b9d 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
@@ -267,38 +268,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 +776,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/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/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index b477905..612be8e 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;
@@ -52,7 +53,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 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 =
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..dff2c5d 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;
@@ -38,7 +39,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Collections;
@@ -109,7 +110,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;
@@ -167,6 +168,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 +203,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);
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 7c73be5..1a77674 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;
@@ -116,8 +116,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 +216,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 +239,19 @@
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;
- }
}
+ Log.d("b/198965093", "handleInfoChange"
+ + "\n\tchange: 0b" + Integer.toBinaryString(change)
+ + "\n\tConfiguration diff: 0x" + Integer.toHexString(
+ newInfo.mConfiguration.diff(oldInfo.mConfiguration)));
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,71 @@
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) {
+ // TODO(b/198965093): Remove after investigation
+ private Configuration mConfiguration;
+
+ 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);
+
+ // TODO(b/198965093): Remove after investigation
+ mConfiguration = config;
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("b/198965093", "Unexpected normalizedDisplayInfo found, invalidating cache");
+ mPerDisplayBounds.clear();
+ mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext));
+ cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ if (cachedValue == null) {
+ Log.e("b/198965093", "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));
+ Log.e("b/198965093", "mConfiguration: " + mConfiguration);
+ Log.d("b/198965093", "displayInfo: " + displayInfo);
+ Log.d("b/198965093", "realBounds: " + realBounds);
+ Log.d("b/198965093", "normalizedDisplayInfo: " + normalizedDisplayInfo);
+ mPerDisplayBounds.forEach((key, value) -> Log.d("b/198965093",
+ "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
}
/**
@@ -369,13 +375,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/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/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/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/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index bdf646b..daa67a9 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -16,27 +16,24 @@
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_HEADER;
+import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_SEARCH_HEADER;
+
import android.content.Context;
import android.graphics.Point;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.TableLayout;
+import androidx.annotation.NonNull;
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 +48,14 @@
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();
+ private final SpacingDecoration mSpacingDecoration;
public WidgetsRecyclerView(Context context) {
this(context, null);
@@ -72,12 +71,8 @@
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);
+ mSpacingDecoration = new SpacingDecoration(context);
+ addItemDecoration(mSpacingDecoration);
}
@Override
@@ -138,67 +133,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 +180,27 @@
* <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)) {
+ for (int i = 0; i < childCount; 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) + mSpacingDecoration.getSpacing(i, type);
}
return totalItemsHeight;
}
@@ -283,4 +216,31 @@
*/
int getHeaderViewHeight();
}
+
+ private static class SpacingDecoration extends RecyclerView.ItemDecoration {
+
+ private final int mSpacingBetweenEntries;
+
+ SpacingDecoration(@NonNull Context context) {
+ mSpacingBetweenEntries =
+ context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
+ }
+
+ @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 = parent.getChildAdapterPosition(view);
+ outRect.top += getSpacing(position, parent.getAdapter().getItemViewType(position));
+ }
+
+ public int getSpacing(int position, int type) {
+ boolean isHeader = type == VIEW_TYPE_WIDGETS_SEARCH_HEADER
+ || type == VIEW_TYPE_WIDGETS_HEADER;
+ return position > 0 && isHeader ? mSpacingBetweenEntries : 0;
+ }
+ }
}
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/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 8a97c6b..e00b569 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -53,7 +53,6 @@
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -199,8 +198,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 +375,7 @@
@Test
@PortraitLandscape
+ @ScreenRecord
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 +388,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 +399,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 +424,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 +510,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();
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index eb7f05b..e919740 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -140,6 +140,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.