Merge "Update scale logic for desktop tasks in recents animation" into main
diff --git a/Android.bp b/Android.bp
index 4dddbf6..28eee94 100644
--- a/Android.bp
+++ b/Android.bp
@@ -136,24 +136,6 @@
min_sdk_version: min_launcher3_sdk_version,
}
-aconfig_declarations {
- name: "launcher_flags",
- package: "com.google.android.platform.launcher.aconfig.flags",
- srcs: ["launcher.aconfig"],
-}
-
-java_aconfig_library {
- name: "launcher_flags_lib",
- aconfig_declarations: "launcher_flags",
-}
-
-java_aconfig_library {
- name: "launcher_flags_lib_test",
- aconfig_declarations: "launcher_flags",
- test: true
-}
-
-
// Library with all the dependencies for building Launcher3
android_library {
name: "Launcher3ResLib",
@@ -185,13 +167,14 @@
//
// Build rule for Launcher3 dependencies lib.
//
-java_defaults {
- name: "Launcher3CommonDepsDefault",
+android_library {
+ name: "Launcher3CommonDepsLib",
srcs: ["src_build_config/**/*.java"],
static_libs: [
"Launcher3ResLib",
"launcher-testing-shared",
- "animationlib"
+ "animationlib",
+ "com_android_launcher3_flags_lib",
],
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
@@ -202,35 +185,13 @@
}
//
-// Build rule for Launcher3 dependencies lib.
-//
-android_library {
- name: "Launcher3CommonDepsLib",
- defaults: ["Launcher3CommonDepsDefault"],
- static_libs: [
- "launcher_flags_lib",
- ],
-}
-
-//
-// Build rule for Launcher3 dependencies lib for test and debug.
-//
-android_library {
- name: "Launcher3CommonDepsLibDebug",
- defaults: ["Launcher3CommonDepsDefault"],
- static_libs: [
- "launcher_flags_lib_test",
- ],
-}
-
-//
// Build rule for Launcher3 app.
//
android_app {
name: "Launcher3",
static_libs: [
- "Launcher3CommonDepsLibDebug",
+ "Launcher3CommonDepsLib",
],
srcs: [
":launcher-src",
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
new file mode 100644
index 0000000..dc30a35
--- /dev/null
+++ b/aconfig/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+ name: "com_android_launcher3_flags",
+ package: "com.android.launcher3",
+ srcs: ["**/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com_android_launcher3_flags_lib",
+ aconfig_declarations: "com_android_launcher3_flags",
+}
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
new file mode 100644
index 0000000..aca1b3b
--- /dev/null
+++ b/aconfig/launcher.aconfig
@@ -0,0 +1,15 @@
+package: "com.android.launcher3"
+
+flag {
+ name: "enable_expanding_pause_work_button"
+ namespace: "launcher"
+ description: "Expand and collapse pause work button while scrolling."
+ bug: "270390779"
+}
+
+flag {
+ name: "enable_twoline_allapps"
+ namespace: "launcher"
+ description: "Enables two line label inside all apps."
+ bug: "270390937"
+}
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index f99155f..29b24b7 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -25,7 +25,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.system.Os;
-import android.util.Log;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
@@ -62,7 +61,6 @@
public void onActivityCreated(Activity activity, Bundle bundle) {
sActivities.put(activity, true);
++sActivitiesCreatedCount;
- Log.d(TestProtocol.FLAKY_ACTIVITY_COUNT, "onActivityCreated", new Exception());
}
@Override
diff --git a/launcher.aconfig b/launcher.aconfig
deleted file mode 100644
index cab193c..0000000
--- a/launcher.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.google.android.platform.launcher.aconfig.flags"
-
-flag {
- name: "enable_expanding_pause_work_button"
- namespace: "launcher"
- description: "Expand and collapse pause work button while scrolling."
- bug: "270390779"
-}
diff --git a/quickstep/res/layout/taskbar_divider_popup_menu.xml b/quickstep/res/layout/taskbar_divider_popup_menu.xml
index 00e47c9..4348a47 100644
--- a/quickstep/res/layout/taskbar_divider_popup_menu.xml
+++ b/quickstep/res/layout/taskbar_divider_popup_menu.xml
@@ -19,7 +19,7 @@
android:layout_width="@dimen/taskbar_pinning_popup_menu_width"
android:layout_height="wrap_content"
android:focusable="true"
- android:background="@drawable/popup_background_material_u"
+ android:background="@drawable/popup_background"
android:orientation="vertical">
<LinearLayout
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index c6c4dde..f8ea932 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -159,6 +159,7 @@
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -225,7 +226,8 @@
private final float mClosingWindowTransY;
private final float mMaxShadowRadius;
- private final StartingWindowListener mStartingWindowListener = new StartingWindowListener();
+ private final StartingWindowListener mStartingWindowListener =
+ new StartingWindowListener(this);
private DeviceProfile mDeviceProfile;
@@ -278,7 +280,6 @@
}
};
- mStartingWindowListener.setTransitionManager(this);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
mStartingWindowListener);
}
@@ -310,8 +311,8 @@
mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
ItemInfo tag = (ItemInfo) v.getTag();
if (tag != null && tag.shouldUseBackgroundAnimation()) {
- ContainerAnimationRunner containerAnimationRunner =
- ContainerAnimationRunner.from(v, mStartingWindowListener, onEndCallback);
+ ContainerAnimationRunner containerAnimationRunner = ContainerAnimationRunner.from(
+ v, mLauncher, mStartingWindowListener, onEndCallback);
if (containerAnimationRunner != null) {
mAppLaunchRunner = containerAnimationRunner;
}
@@ -1152,7 +1153,6 @@
public void onActivityDestroyed() {
unregisterRemoteAnimations();
unregisterRemoteTransitions();
- mStartingWindowListener.setTransitionManager(null);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
}
@@ -1775,8 +1775,8 @@
}
@Nullable
- private static ContainerAnimationRunner from(
- View v, StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
+ private static ContainerAnimationRunner from(View v, Launcher launcher,
+ StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
View viewToUse = findViewWithBackground(v);
if (viewToUse == null) {
viewToUse = v;
@@ -1801,8 +1801,13 @@
}
};
- ActivityLaunchAnimator.Callback callback = task -> ColorUtils.setAlphaComponent(
- startingWindowListener.getBackgroundColor(), 255);
+ ActivityLaunchAnimator.Callback callback = task -> {
+ final int backgroundColor =
+ startingWindowListener.mBackgroundColor == Color.TRANSPARENT
+ ? launcher.getScrimView().getBackgroundColor()
+ : startingWindowListener.mBackgroundColor;
+ return ColorUtils.setAlphaComponent(backgroundColor, 255);
+ };
ActivityLaunchAnimator.Listener listener = new ActivityLaunchAnimator.Listener() {
@Override
@@ -1912,25 +1917,22 @@
}
}
- private class StartingWindowListener extends IStartingWindowListener.Stub {
- private QuickstepTransitionManager mTransitionManager;
+ private static class StartingWindowListener extends IStartingWindowListener.Stub {
+ private final WeakReference<QuickstepTransitionManager> mTransitionManagerRef;
private int mBackgroundColor;
- public void setTransitionManager(QuickstepTransitionManager transitionManager) {
- mTransitionManager = transitionManager;
+ private StartingWindowListener(QuickstepTransitionManager transitionManager) {
+ mTransitionManagerRef = new WeakReference<>(transitionManager);
}
@Override
public void onTaskLaunching(int taskId, int supportedType, int color) {
- mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
+ QuickstepTransitionManager transitionManager = mTransitionManagerRef.get();
+ if (transitionManager != null) {
+ transitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
+ }
mBackgroundColor = color;
}
-
- public int getBackgroundColor() {
- return mBackgroundColor == Color.TRANSPARENT
- ? mLauncher.getScrimView().getBackgroundColor()
- : mBackgroundColor;
- }
}
/**
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 619bef2..87a9ecb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -27,6 +27,7 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.ComponentName;
+import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.ViewGroup;
@@ -74,6 +75,7 @@
SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener,
DragSource, ViewGroup.OnHierarchyChangeListener {
+ private static final String TAG = "HotseatPredictionController";
private static final int FLAG_UPDATE_PAUSED = 1 << 0;
private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
@@ -183,6 +185,7 @@
}
private void fillGapsWithPrediction(boolean animate) {
+ Log.d(TAG, "fillGapsWithPrediction");
if (mPauseFlags != 0) {
return;
}
@@ -207,12 +210,16 @@
View child = mHotseat.getChildAt(
mHotseat.getCellXFromOrder(rank),
mHotseat.getCellYFromOrder(rank));
+ Log.d(TAG, "Hotseat app child is: " + child + " and isPredictedIcon() evaluates to"
+ + ": " + isPredictedIcon(child));
if (child != null && !isPredictedIcon(child)) {
continue;
}
if (mPredictedItems.size() <= predictionIndex) {
// Remove predicted apps from the past
+ Log.d(TAG, "Remove predicted apps from the past\nPrediction Index: "
+ + predictionIndex);
if (isPredictedIcon(child)) {
mHotseat.removeView(child);
}
@@ -220,6 +227,11 @@
}
WorkspaceItemInfo predictedItem =
(WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
+ Log.d(TAG, "Predicted item is: " + predictedItem);
+ if (child != null) {
+ Log.d(TAG, "Predicted item is enabled: " + child.isEnabled());
+ }
+
if (isPredictedIcon(child) && child.isEnabled()) {
PredictedAppIcon icon = (PredictedAppIcon) child;
boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
@@ -239,6 +251,7 @@
}
private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
+ Log.d(TAG, "bindItems to hotseat: " + itemsToAdd);
AnimatorSet animationSet = new AnimatorSet();
for (WorkspaceItemInfo item : itemsToAdd) {
PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
@@ -292,8 +305,10 @@
public void setPredictedItems(FixedContainerItems items) {
mPredictedItems = new ArrayList(items.items);
if (mPredictedItems.isEmpty()) {
+ Log.d(TAG, "Predicted items is initially empty");
HotseatRestoreHelper.restoreBackup(mLauncher);
}
+ Log.d(TAG, "Predicted items: " + mPredictedItems);
fillGapsWithPrediction();
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index ecf483c..d7a4f76 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -106,8 +106,7 @@
* Whether desktop mode is supported.
*/
private boolean isDesktopModeSupported() {
- return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
- || SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
+ return SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
index 885afff..29c5204 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
+import android.content.pm.ActivityInfo.Config;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -56,6 +57,11 @@
@Override
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ super.init(controllers);
+ }
+
+ @Override
+ protected void setupController() {
mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarHeight;
// Quick settings and notifications buttons
@@ -72,4 +78,7 @@
/** Cleans up on destroy */
@Override
public void onDestroy() { }
+
+ @Override
+ public void onConfigurationChanged(@Config int configChanges) { }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 072fc30..dda8446 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -109,7 +109,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED
+ DesktopTaskView.DESKTOP_MODE_SUPPORTED
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
@@ -136,7 +136,7 @@
// Hide all desktop tasks and show them on the hidden tile
int hiddenDesktopTasks = 0;
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
hiddenDesktopTasks = desktopTask.tasks.size();
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index fcd8c80..fa16b61 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -219,11 +219,15 @@
*/
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ setupController();
+ }
+
+ protected void setupController() {
boolean isThreeButtonNav = mContext.isThreeButtonNav();
DeviceProfile deviceProfile = mContext.getDeviceProfile();
Resources resources = mContext.getResources();
Point p = !mContext.isUserSetupComplete()
- ? new Point(0, controllers.taskbarActivityContext.getSetupWindowHeight())
+ ? new Point(0, mControllers.taskbarActivityContext.getSetupWindowHeight())
: DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
TaskbarManager.isPhoneMode(deviceProfile));
mNavButtonsView.getLayoutParams().height = p.y;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 993f13e..6d86b1e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -51,7 +51,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.util.AssistUtilsBase;
+import com.android.quickstep.util.AssistUtils;
import com.android.quickstep.views.DesktopTaskView;
import java.io.PrintWriter;
@@ -272,7 +272,7 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
@@ -313,7 +313,7 @@
return;
}
// Attempt to start Assist with AssistUtils, otherwise fall back to SysUi's implementation.
- if (!AssistUtilsBase.newInstance(mService.getApplicationContext()).tryStartAssistOverride(
+ if (!AssistUtils.newInstance(mService.getApplicationContext()).tryStartAssistOverride(
INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
Bundle args = new Bundle();
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 512b77a..a667dca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
@@ -163,19 +162,9 @@
.filter(Objects::nonNull)
.collect(Collectors.toList());
- if (ENABLE_MATERIAL_U_POPUP.get()) {
- container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
- R.layout.popup_container_material_u, context.getDragLayer(), false);
- container.populateAndShowRowsMaterialU(icon, deepShortcutCount, systemShortcuts);
- } else {
- container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
+ container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
R.layout.popup_container, context.getDragLayer(), false);
- container.populateAndShow(
- icon,
- deepShortcutCount,
- mPopupDataProvider.getNotificationKeysForItem(item),
- systemShortcuts);
- }
+ container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
container.addOnAttachStateChangeListener(
new PopupLiveUpdateHandler<BaseTaskbarContext>(context, container) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index ffe077b..c482911 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -280,7 +280,7 @@
// the position of the bubble when the bar is fully expanded
final float expandedX = i * (mIconSize + mIconSpacing);
// the position of the bubble when the bar is fully collapsed
- final float collapsedX = i * mIconOverlapAmount;
+ final float collapsedX = i == 0 ? 0 : mIconOverlapAmount;
if (mIsBarExpanded) {
// where the bubble will end up when the animation ends
@@ -292,12 +292,22 @@
}
// When we're expanded, we're not stacked so we're not behind the stack
bv.setBehindStack(false, animate);
+ bv.setAlpha(1);
} else {
final float targetX = currentWidth - collapsedWidth + collapsedX;
bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
// If we're not the first bubble we're behind the stack
bv.setBehindStack(i > 0, animate);
+ // If we're fully collapsed, hide all bubbles except for the first 2. If there are
+ // only 2 bubbles, hide the second bubble as well because it's the overflow.
+ if (widthState == 0) {
+ if (i > 1) {
+ bv.setAlpha(0);
+ } else if (i == 1 && bubbleCount == 2) {
+ bv.setAlpha(0);
+ }
+ }
}
}
@@ -458,7 +468,11 @@
private float collapsedWidth() {
final int childCount = getChildCount();
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
- return mIconSize + ((childCount - 1) * mIconOverlapAmount) + horizontalPadding;
+ // If there are more than 2 bubbles, the first 2 should be visible when collapsed.
+ // Otherwise just the first bubble should be visible because we don't show the overflow.
+ return childCount > 2
+ ? mIconSize + mIconOverlapAmount + horizontalPadding
+ : mIconSize + horizontalPadding;
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4c701c7..b50ab97 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -73,6 +73,7 @@
import android.os.IBinder;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.View;
@@ -191,6 +192,8 @@
private static final String TRACE_RELAYOUT_CLASS =
SystemProperties.get("persist.debug.trace_request_layout_class", null);
+ private static final String TAG = "QuickstepLauncher";
+
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
@@ -437,6 +440,7 @@
@Override
public void bindExtraContainerItems(FixedContainerItems item) {
+ Log.d(TAG, "Bind extra container items");
if (item.containerId == Favorites.CONTAINER_PREDICTION) {
mAllAppsPredictions = item;
PredictionRowView<?> predictionRowView =
@@ -444,6 +448,7 @@
PredictionRowView.class);
predictionRowView.setPredictedApps(item.items);
} else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ Log.d(TAG, "Bind extra container item is hotseat prediction");
mHotseatPredictionController.setPredictedItems(item);
} else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
getPopupDataProvider().setRecommendedWidgets(item.items);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 4075388..ca598c8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -119,9 +119,6 @@
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
if (fromState == NORMAL && mDidTouchStartInNavBar) {
return HINT_STATE;
- } else if (fromState == OVERVIEW && isDragTowardPositive) {
- // Don't allow swiping up to all apps.
- return OVERVIEW;
}
return super.getTargetState(fromState, isDragTowardPositive);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 454a1f5..e30fe66 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -96,8 +96,6 @@
return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
? mLauncher.getStateManager().getLastState()
: NORMAL;
- } else if (fromState == OVERVIEW) {
- return isDragTowardPositive ? OVERVIEW : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
return ALL_APPS;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 1ef4039..9b8dd5f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1161,7 +1161,7 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
// Notify the SysUI to stash desktop apps if they are visible
DesktopVisibilityController desktopVisibilityController =
mActivityInterface.getDesktopVisibilityController();
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index f1660ee..857c831 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -58,6 +58,8 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
+import java.lang.ref.WeakReference;
+
/**
* Controls the animation of swiping back and returning to launcher.
*
@@ -105,7 +107,7 @@
private boolean mAnimatorSetInProgress = false;
private float mBackProgress = 0;
private boolean mBackInProgress = false;
- private IOnBackInvokedCallback mBackCallback;
+ private OnBackInvokedCallbackStub mBackCallback;
private IRemoteAnimationFinishedCallback mAnimationFinishedCallback;
private BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
private SurfaceControl mScrimLayer;
@@ -137,65 +139,104 @@
* @param handler Handler to the thread to run the animations on.
*/
public void registerBackCallbacks(Handler handler) {
- mBackCallback = new IOnBackInvokedCallback.Stub() {
- @Override
- public void onBackCancelled() {
- handler.post(() -> {
+ mBackCallback = new OnBackInvokedCallbackStub(handler, mProgressAnimator, this);
+ SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback,
+ new RemoteAnimationRunnerStub(this));
+ }
+
+ private static class OnBackInvokedCallbackStub extends IOnBackInvokedCallback.Stub {
+ private Handler mHandler;
+ private BackProgressAnimator mProgressAnimator;
+ // LauncherBackAnimationController has strong reference to Launcher activity, the binder
+ // callback should not hold strong reference to it to avoid memory leak.
+ private WeakReference<LauncherBackAnimationController> mControllerRef;
+
+ private OnBackInvokedCallbackStub(
+ Handler handler,
+ BackProgressAnimator progressAnimator,
+ LauncherBackAnimationController controller) {
+ mHandler = handler;
+ mProgressAnimator = progressAnimator;
+ mControllerRef = new WeakReference<>(controller);
+ }
+
+ @Override
+ public void onBackCancelled() {
+ mHandler.post(() -> {
+ LauncherBackAnimationController controller = mControllerRef.get();
+ if (controller != null) {
mProgressAnimator.onBackCancelled(
- LauncherBackAnimationController.this::resetPositionAnimated);
- });
- }
-
- @Override
- public void onBackInvoked() {
- handler.post(() -> {
- startTransition();
- mProgressAnimator.reset();
- });
- }
-
- @Override
- public void onBackProgressed(BackMotionEvent backEvent) {
- handler.post(() -> {
- mProgressAnimator.onBackProgressed(backEvent);
- });
- }
-
- @Override
- public void onBackStarted(BackMotionEvent backEvent) {
- handler.post(() -> {
- startBack(backEvent);
- mProgressAnimator.onBackStarted(backEvent, event -> {
- mBackProgress = event.getProgress();
- // TODO: Update once the interpolation curve spec is finalized.
- mBackProgress =
- 1 - (1 - mBackProgress) * (1 - mBackProgress) * (1
- - mBackProgress);
- updateBackProgress(mBackProgress, event);
- });
- });
- }
- };
-
- final IRemoteAnimationRunner runner = new IRemoteAnimationRunner.Stub() {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) {
- for (final RemoteAnimationTarget target : apps) {
- if (MODE_CLOSING == target.mode) {
- mBackTarget = target;
- break;
- }
+ controller::resetPositionAnimated);
}
- mAnimationFinishedCallback = finishedCallback;
+ });
+ }
+
+ @Override
+ public void onBackInvoked() {
+ mHandler.post(() -> {
+ LauncherBackAnimationController controller = mControllerRef.get();
+ if (controller != null) {
+ controller.startTransition();
+ }
+ mProgressAnimator.reset();
+ });
+ }
+
+ @Override
+ public void onBackProgressed(BackMotionEvent backMotionEvent) {
+ mHandler.post(() -> {
+ mProgressAnimator.onBackProgressed(backMotionEvent);
+ });
+ }
+
+ @Override
+ public void onBackStarted(BackMotionEvent backEvent) {
+ mHandler.post(() -> {
+ LauncherBackAnimationController controller = mControllerRef.get();
+ if (controller != null) {
+ controller.startBack(backEvent);
+ mProgressAnimator.onBackStarted(backEvent, event -> {
+ float backProgress = event.getProgress();
+ // TODO: Update once the interpolation curve spec is finalized.
+ controller.mBackProgress =
+ 1 - (1 - backProgress) * (1 - backProgress) * (1
+ - backProgress);
+ controller.updateBackProgress(controller.mBackProgress, event);
+ });
+ }
+ });
+ }
+ }
+
+ private static class RemoteAnimationRunnerStub extends IRemoteAnimationRunner.Stub {
+
+ // LauncherBackAnimationController has strong reference to Launcher activity, the binder
+ // callback should not hold strong reference to it to avoid memory leak.
+ private WeakReference<LauncherBackAnimationController> mControllerRef;
+
+ private RemoteAnimationRunnerStub(LauncherBackAnimationController controller) {
+ mControllerRef = new WeakReference<>(controller);
+ }
+
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ LauncherBackAnimationController controller = mControllerRef.get();
+ if (controller == null) {
+ return;
}
+ for (final RemoteAnimationTarget target : apps) {
+ if (MODE_CLOSING == target.mode) {
+ controller.mBackTarget = target;
+ break;
+ }
+ }
+ controller.mAnimationFinishedCallback = finishedCallback;
+ }
- @Override
- public void onAnimationCancelled() {}
- };
-
- SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback, runner);
+ @Override
+ public void onAnimationCancelled() {}
}
private void resetPositionAnimated() {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 34817c0..1c74fbe 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,7 +17,7 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.views.DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
@@ -270,7 +270,7 @@
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (DESKTOP_IS_PROTO2_ENABLED && rawTask.getType() == TYPE_FREEFORM) {
+ if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index f0308df..99dd634 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -62,7 +62,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.quickstep.util.AssistUtilsBase;
+import com.android.quickstep.util.AssistUtils;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -252,7 +252,7 @@
setUnfoldAnimationListener(mUnfoldAnimationListener);
setDesktopTaskListener(mDesktopTaskListener);
setAssistantOverridesRequested(
- AssistUtilsBase.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
+ AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
}
/**
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 901690b..4177ced 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -346,7 +346,6 @@
private boolean isAvailable(BaseDraggingActivity activity, int displayId) {
return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity)
- && !SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
&& !SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index af49774..97e484a 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -38,8 +38,6 @@
import static com.android.launcher3.QuickstepTransitionManager.SPLIT_DIVIDER_ANIM_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
@@ -427,7 +425,6 @@
int initialTaskId, int secondTaskId, @NonNull TransitionInfo transitionInfo,
SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
if (launchingTaskView != null) {
- testLogD(LAUNCH_SPLIT_PAIR, "composeRecentsSplitLaunchAnimator taskView not-null");
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
@@ -461,7 +458,6 @@
for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
if (change.getTaskInfo() == null) {
- testLogD(LAUNCH_SPLIT_PAIR, "changeTaskInfo null; change: " + change);
continue;
}
final int taskId = change.getTaskInfo().taskId;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index cd88894..c1680de 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -117,7 +117,7 @@
import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
-import com.android.quickstep.util.AssistUtilsBase;
+import com.android.quickstep.util.AssistUtils;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -289,7 +289,7 @@
@Override
public void onAssistantOverrideInvoked(int invocationType) {
executeForTouchInteractionService(tis -> {
- if (!AssistUtilsBase.newInstance(tis).tryStartAssistOverride(invocationType)) {
+ if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) {
Log.w(TAG, "Failed to invoke Assist override");
}
});
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 5c5b9ca..7a2b343 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -18,6 +18,8 @@
import android.content.Context;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -33,12 +35,15 @@
}
/**
- * Called when nav handle is long pressed.
- *
- * @return if the long press was consumed, meaning other input consumers should receive a
- * cancel event
+ * Called when nav handle is long pressed to get the Runnable that should be executed by the
+ * caller to invoke long press behavior. If null is returned that means long press couldn't be
+ * handled.
+ * <p>
+ * A Runnable is returned here to ensure the InputConsumer can call
+ * {@link android.view.InputMonitor#pilferPointers()} before invoking the long press behavior
+ * since pilfering can break the long press behavior.
*/
- public boolean onLongPress() {
- return false;
+ public @Nullable Runnable getLongPressRunnable() {
+ return null;
}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index 542dea1..a9accb7 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -38,8 +38,8 @@
public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor) {
super(delegate, inputMonitor);
- mNavHandleWidth = context.getResources()
- .getDimensionPixelSize(R.dimen.navigation_home_handle_width);
+ mNavHandleWidth = context.getResources().getDimensionPixelSize(
+ R.dimen.navigation_home_handle_width);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context);
@@ -48,8 +48,11 @@
@Override
public void onLongPress(MotionEvent motionEvent) {
if (isInArea(motionEvent.getRawX())) {
- if (mNavHandleLongPressHandler.onLongPress()) {
+ Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable();
+ if (longPressRunnable != null) {
setActive(motionEvent);
+
+ longPressRunnable.run();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/AssistUtilsBase.java b/quickstep/src/com/android/quickstep/util/AssistUtils.java
similarity index 84%
rename from quickstep/src/com/android/quickstep/util/AssistUtilsBase.java
rename to quickstep/src/com/android/quickstep/util/AssistUtils.java
index 7b27020..11b6ea7 100644
--- a/quickstep/src/com/android/quickstep/util/AssistUtilsBase.java
+++ b/quickstep/src/com/android/quickstep/util/AssistUtils.java
@@ -21,13 +21,13 @@
import com.android.launcher3.util.ResourceBasedOverride;
/** Utilities to work with Assistant functionality. */
-public class AssistUtilsBase implements ResourceBasedOverride {
+public class AssistUtils implements ResourceBasedOverride {
- public AssistUtilsBase() {}
+ public AssistUtils() {}
/** Creates AssistUtils as specified by overrides */
- public static AssistUtilsBase newInstance(Context context) {
- return Overrides.getObject(AssistUtilsBase.class, context, R.string.assist_utils_class);
+ public static AssistUtils newInstance(Context context) {
+ return Overrides.getObject(AssistUtils.class, context, R.string.assist_utils_class);
}
/** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 6865935..16fe07d 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -18,9 +18,8 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM;
-import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
@@ -94,8 +93,8 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
-import com.android.quickstep.views.SplitInstructionsView;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.SplitInstructionsView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -531,10 +530,6 @@
mSplitFromDesktopController = new SplitFromDesktopController(launcher);
}
- public void enterSplitFromDesktop(ActivityManager.RunningTaskInfo taskInfo) {
- mSplitFromDesktopController.enterSplitSelect(taskInfo);
- }
-
private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
@Nullable Consumer<Boolean> callback, String transitionName) {
final RemoteSplitLaunchTransitionRunner animationRunner =
@@ -603,7 +598,6 @@
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
- testLogD(LAUNCH_SPLIT_PAIR, "Received split startAnimation");
final Runnable finishAdapter = () -> {
try {
finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
@@ -772,9 +766,11 @@
R.dimen.split_placeholder_inset);
mSplitSelectListener = new ISplitSelectListener.Stub() {
@Override
- public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
+ public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
+ int splitPosition, Rect taskBounds) {
if (!ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get()) return false;
- MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo));
+ MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
+ taskBounds));
return true;
}
};
@@ -784,8 +780,11 @@
/**
* Enter split select from desktop mode.
* @param taskInfo the desktop task to move to split stage
+ * @param splitPosition the stage position used for this transition
+ * @param taskBounds the bounds of the task, used for {@link FloatingTaskView} animation
*/
- public void enterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
+ public void enterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
+ int splitPosition, Rect taskBounds) {
mTaskInfo = taskInfo;
String packageName = mTaskInfo.realActivity.getPackageName();
PackageManager pm = mLauncher.getApplicationContext().getPackageManager();
@@ -801,7 +800,7 @@
false /* allowMinimizeSplitScreen */);
DesktopSplitRecentsAnimationListener listener =
- new DesktopSplitRecentsAnimationListener();
+ new DesktopSplitRecentsAnimationListener(splitPosition, taskBounds);
MAIN_EXECUTOR.execute(() -> {
callbacks.addListener(listener);
@@ -817,12 +816,23 @@
private class DesktopSplitRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final Rect mTempRect = new Rect();
+ private final RectF mTaskBounds = new RectF();
+ private final int mSplitPosition;
+
+ DesktopSplitRecentsAnimationListener(int splitPosition, Rect taskBounds) {
+ mSplitPosition = splitPosition;
+ mTaskBounds.set(taskBounds);
+ }
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
- setInitialTaskSelect(mTaskInfo, STAGE_POSITION_BOTTOM_OR_RIGHT,
- null, LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM);
+ StatsLogManager.LauncherEvent launcherDesktopSplitEvent =
+ mSplitPosition == STAGE_POSITION_BOTTOM_OR_RIGHT ?
+ LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM :
+ LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP;
+ setInitialTaskSelect(mTaskInfo, mSplitPosition,
+ null, launcherDesktopSplitEvent);
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
@@ -831,14 +841,12 @@
PendingAnimation anim = new PendingAnimation(
SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
- RectF startingTaskRect = new RectF(mTaskInfo.configuration.windowConfiguration
- .getBounds());
final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
mLauncher, mLauncher.getDragLayer(),
null /* thumbnail */,
mAppIcon, new RectF());
floatingTaskView.setAlpha(1);
- floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
+ floatingTaskView.addStagingAnimation(anim, mTaskBounds, mTempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
setFirstFloatingTaskView(floatingTaskView);
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 5f3fd0c..66557cc 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -68,18 +68,10 @@
// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
public class DesktopTaskView extends TaskView {
- /** Flag to indicate whether desktop windowing proto 1 is enabled */
- private static final boolean DESKTOP_IS_PROTO1_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
-
/** Flag to indicate whether desktop windowing proto 2 is enabled */
- public static final boolean DESKTOP_IS_PROTO2_ENABLED = SystemProperties.getBoolean(
+ public static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
"persist.wm.debug.desktop_mode_2", false);
- /** Flags to indicate whether desktop mode is available on the device */
- public static final boolean DESKTOP_MODE_SUPPORTED =
- DESKTOP_IS_PROTO1_ENABLED || DESKTOP_IS_PROTO2_ENABLED;
-
private static final String TAG = DesktopTaskView.class.getSimpleName();
private static final boolean DEBUG = false;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index be9da34..7cf47ce 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1615,7 +1615,7 @@
mMovingTaskView = null;
runningTaskView.resetPersistentViewTransforms();
int frontTaskIndex = 0;
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED && mDesktopTaskView != null
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && mDesktopTaskView != null
&& !runningTaskView.isDesktopTask()) {
// If desktop mode is enabled, desktop task view is pinned at first position if present.
// Move running task to position 1.
@@ -1755,7 +1755,7 @@
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
// Check if we have apps on the desktop
if (desktopTask != null && !desktopTask.tasks.isEmpty()) {
// If we are actively choosing apps for split, skip the desktop tile
@@ -2060,7 +2060,7 @@
mLastComputedGridSize);
mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
mLastComputedGridTaskSize, mOrientationHandler);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
mSizeStrategy.calculateDesktopTaskSize(mActivity, mActivity.getDeviceProfile(),
mLastComputedDesktopTaskSize);
}
@@ -2739,7 +2739,7 @@
}
private boolean hasDesktopTask(Task[] runningTasks) {
- if (!DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (!DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
return false;
}
for (Task task : runningTasks) {
@@ -3812,33 +3812,19 @@
taskViewIdArray.removeValue(
finalNextFocusedTaskView.getTaskViewId());
}
- try {
- if (snappedIndex < taskViewIdArray.size()) {
- taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
- } else if (snappedIndex == taskViewIdArray.size()) {
- // If the snapped task is the last item from the
- // dismissed row,
- // snap to the same column in the other grid row
- IntArray inverseRowTaskViewIdArray =
- isSnappedTaskInTopRow ? getBottomRowIdArray()
- : getTopRowIdArray();
- if (snappedIndex < inverseRowTaskViewIdArray.size()) {
- taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
- snappedIndex);
- }
+ if (snappedIndex < taskViewIdArray.size()) {
+ taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
+ } else if (snappedIndex == taskViewIdArray.size()) {
+ // If the snapped task is the last item from the
+ // dismissed row,
+ // snap to the same column in the other grid row
+ IntArray inverseRowTaskViewIdArray =
+ isSnappedTaskInTopRow ? getBottomRowIdArray()
+ : getTopRowIdArray();
+ if (snappedIndex < inverseRowTaskViewIdArray.size()) {
+ taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
+ snappedIndex);
}
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new IllegalStateException(
- "b/269956477 invalid snappedIndex"
- + "\nsnappedTaskViewId: "
- + snappedTaskViewId
- + "\nfocusedTaskViewId: "
- + mFocusedTaskViewId
- + "\ntopRowIdArray: "
- + getTopRowIdArray().toConcatString()
- + "\nbottomRowIdArray: "
- + getBottomRowIdArray().toConcatString(),
- e);
}
}
}
@@ -4633,7 +4619,7 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4657,7 +4643,7 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4794,8 +4780,9 @@
} else {
resetFromSplitSelectionState();
}
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
});
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
});
mSecondSplitHiddenView = containerTaskView;
@@ -4862,7 +4849,7 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
mSplitHiddenTaskView = null;
}
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
updateDesktopTaskVisibility(true /* visible */);
}
}
@@ -5411,7 +5398,7 @@
}
private int getFirstViewIndex() {
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED && mDesktopTaskView != null) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED && mDesktopTaskView != null) {
// Desktop task is at position 0, that is the first view
return 0;
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 40d0ac7..25f90ca 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -185,7 +185,6 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- @ScreenRecord // b/195673272
@PlatinumTest(focusArea = "launcher")
public void testOverviewActions() throws Exception {
// Experimenting for b/165029151:
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 92b598b..a90c326 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -108,7 +108,8 @@
@Test
public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception {
- assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+ assumeTrue("App pairs feature is currently not enabled, no test needed",
+ FeatureFlags.ENABLE_APP_PAIRS.get());
createAndLaunchASplitPair();
@@ -122,7 +123,8 @@
@Test
public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception {
- assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+ assumeTrue("App pairs feature is currently not enabled, no test needed",
+ FeatureFlags.ENABLE_APP_PAIRS.get());
startAppFast(CALCULATOR_APP_PACKAGE);
diff --git a/res/drawable/popup_background_material_u.xml b/res/drawable/popup_background.xml
similarity index 100%
rename from res/drawable/popup_background_material_u.xml
rename to res/drawable/popup_background.xml
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index b175d17..6c1a2f7 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -13,10 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<com.android.launcher3.shortcuts.DeepShortcutView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/deep_shortcut_material"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height"
android:elevation="@dimen/deep_shortcuts_elevation"
@@ -31,12 +31,11 @@
android:textAlignment="viewStart"
android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
android:paddingEnd="@dimen/popup_padding_end"
- android:drawableEnd="@drawable/ic_drag_handle"
android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
android:singleLine="true"
android:ellipsize="end"
android:textSize="14sp"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?attr/popupTextColor"
launcher:layoutHorizontal="true"
launcher:iconDisplay="shortcut_popup"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
@@ -48,5 +47,4 @@
android:layout_marginStart="@dimen/popup_padding_start"
android:layout_gravity="start|center_vertical"
android:background="@drawable/ic_deepshortcut_placeholder"/>
-
-</com.android.launcher3.shortcuts.DeepShortcutView>
+</com.android.launcher3.shortcuts.DeepShortcutView>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut_container.xml b/res/layout/deep_shortcut_container.xml
index b6c3f56..bf9124a 100644
--- a/res/layout/deep_shortcut_container.xml
+++ b/res/layout/deep_shortcut_container.xml
@@ -16,7 +16,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/deep_shortcuts_container"
- android:background="@drawable/popup_background_material_u"
+ android:background="@drawable/popup_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="@string/popup_container_iterate_children"
diff --git a/res/layout/deep_shortcut_material_u.xml b/res/layout/deep_shortcut_material_u.xml
deleted file mode 100644
index 2e21ddb..0000000
--- a/res/layout/deep_shortcut_material_u.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.shortcuts.DeepShortcutView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/deep_shortcut_material"
- android:layout_width="@dimen/bg_popup_item_width"
- android:layout_height="@dimen/bg_popup_item_height"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:background="@drawable/middle_item_primary"
- android:theme="@style/PopupItem" >
-
- <com.android.launcher3.shortcuts.DeepShortcutTextView
- style="@style/BaseIcon"
- android:id="@+id/bubble_text"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="start|center_vertical"
- android:textAlignment="viewStart"
- android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
- android:paddingEnd="@dimen/popup_padding_end"
- android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
- android:singleLine="true"
- android:ellipsize="end"
- android:textSize="14sp"
- android:textColor="?attr/popupTextColor"
- launcher:layoutHorizontal="true"
- launcher:iconDisplay="shortcut_popup"
- launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
-
- <View
- android:id="@+id/icon"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size"
- android:layout_marginStart="@dimen/popup_padding_start"
- android:layout_gravity="start|center_vertical"
- android:background="@drawable/ic_deepshortcut_placeholder"/>
-</com.android.launcher3.shortcuts.DeepShortcutView>
\ No newline at end of file
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index 9327287..bf7b126 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -13,27 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<com.android.launcher3.popup.PopupContainerWithArrow
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clipToPadding="false"
android:clipChildren="false"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/deep_shortcuts_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:tag="@string/popup_container_iterate_children"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:orientation="vertical"/>
-
- <com.android.launcher3.notification.NotificationContainer
- android:id="@+id/notification_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"/>
-</com.android.launcher3.popup.PopupContainerWithArrow>
\ No newline at end of file
+ android:clipToPadding="false"
+ android:orientation="vertical"/>
\ No newline at end of file
diff --git a/res/layout/popup_container_material_u.xml b/res/layout/popup_container_material_u.xml
deleted file mode 100644
index d34c500..0000000
--- a/res/layout/popup_container_material_u.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.popup.PopupContainerWithArrow
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/popup_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical"/>
\ No newline at end of file
diff --git a/res/layout/system_shortcut_icons_container.xml b/res/layout/system_shortcut_icons_container.xml
index fa92ba3..a5c0be3 100644
--- a/res/layout/system_shortcut_icons_container.xml
+++ b/res/layout/system_shortcut_icons_container.xml
@@ -17,9 +17,10 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_shortcuts_container"
+ android:tag="@string/popup_container_iterate_children"
android:layout_width="match_parent"
android:layout_height="@dimen/system_shortcut_header_height"
android:orientation="horizontal"
android:gravity="end|center_vertical"
- android:background="@drawable/single_item_primary"
+ android:background="@drawable/popup_background"
android:elevation="@dimen/deep_shortcuts_elevation"/>
diff --git a/res/layout/system_shortcut_icons_container_material_u.xml b/res/layout/system_shortcut_icons_container_material_u.xml
deleted file mode 100644
index fbf18af..0000000
--- a/res/layout/system_shortcut_icons_container_material_u.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/system_shortcuts_container"
- android:tag="@string/popup_container_iterate_children"
- android:layout_width="match_parent"
- android:layout_height="@dimen/system_shortcut_header_height"
- android:orientation="horizontal"
- android:gravity="end|center_vertical"
- android:background="@drawable/popup_background_material_u"
- android:elevation="@dimen/deep_shortcuts_elevation"/>
diff --git a/res/layout/system_shortcut_rows_container.xml b/res/layout/system_shortcut_rows_container.xml
index f992ef5..1940139 100644
--- a/res/layout/system_shortcut_rows_container.xml
+++ b/res/layout/system_shortcut_rows_container.xml
@@ -17,6 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_shortcuts_container"
+ android:background="@drawable/popup_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="@string/popup_container_iterate_children"
diff --git a/res/layout/system_shortcut_rows_container_material_u.xml b/res/layout/system_shortcut_rows_container_material_u.xml
deleted file mode 100644
index 006e280..0000000
--- a/res/layout/system_shortcut_rows_container_material_u.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2023 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/system_shortcuts_container"
- android:background="@drawable/popup_background_material_u"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:tag="@string/popup_container_iterate_children"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:orientation="vertical"/>
diff --git a/res/layout/widget_shortcut_container_material_u.xml b/res/layout/widget_shortcut_container_material_u.xml
index aab34e3..3a49c70 100644
--- a/res/layout/widget_shortcut_container_material_u.xml
+++ b/res/layout/widget_shortcut_container_material_u.xml
@@ -17,7 +17,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_shortcut_container"
- android:background="@drawable/popup_background_material_u"
+ android:background="@drawable/popup_background"
android:layout_width="match_parent"
android:layout_height="@dimen/system_shortcut_header_height"
android:orientation="horizontal"
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 7131452..7b0d71b 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -11,6 +11,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.appwidget.AppWidgetProviderInfo;
@@ -26,12 +27,14 @@
import android.widget.ImageButton;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper.CellPos;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -47,15 +50,18 @@
import java.util.List;
public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
- private static final int SNAP_DURATION = 150;
+ private static final int SNAP_DURATION_MS = 150;
private static final float DIMMED_HANDLE_ALPHA = 0f;
private static final float RESIZE_THRESHOLD = 0.66f;
+ private static final int RESIZE_TRANSITION_DURATION_MS = 150;
private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
"launcher.reconfigurable_widget_education_tip_seen";
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpRect2 = new Rect();
+ private static final int[] sDragLayerLoc = new int[2];
+
private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1;
@@ -124,6 +130,12 @@
private int mTopTouchRegionAdjustment = 0;
private int mBottomTouchRegionAdjustment = 0;
+ private int[] mWidgetViewWindowPos;
+ private final Rect mWidgetViewOldRect = new Rect();
+ private final Rect mWidgetViewNewRect = new Rect();
+ private final @Nullable LauncherAppWidgetHostView.CellChildViewPreLayoutListener
+ mCellChildViewPreLayoutListener;
+
private int mXDown, mYDown;
public AppWidgetResizeFrame(Context context) {
@@ -140,6 +152,18 @@
mLauncher = Launcher.getLauncher(context);
mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
+ mCellChildViewPreLayoutListener = FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()
+ ? (v, left, top, right, bottom) -> {
+ if (mWidgetViewWindowPos == null) {
+ mWidgetViewWindowPos = new int[2];
+ }
+ v.getLocationInWindow(mWidgetViewWindowPos);
+ mWidgetViewOldRect.set(v.getLeft(), v.getTop(), v.getRight(),
+ v.getBottom());
+ mWidgetViewNewRect.set(left, top, right, bottom);
+ }
+ : null;
+
mBackgroundPadding = getResources()
.getDimensionPixelSize(R.dimen.resize_frame_background_padding);
mTouchTargetWidth = 2 * mBackgroundPadding;
@@ -260,6 +284,14 @@
}
}
+ if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
+ mWidgetView.setCellChildViewPreLayoutListener(mCellChildViewPreLayoutListener);
+ mWidgetViewOldRect.set(mWidgetView.getLeft(), mWidgetView.getTop(),
+ mWidgetView.getRight(),
+ mWidgetView.getBottom());
+ mWidgetViewNewRect.set(mWidgetViewOldRect);
+ }
+
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
CellPos presenterPos = mLauncher.getCellPosMapper().mapModelToPresenter(widgetInfo);
@@ -344,22 +376,6 @@
resizeWidgetIfNeeded(false);
- // When the widget resizes in multi-window mode, the translation value changes to maintain
- // a center fit. These overrides ensure the resize frame always aligns with the widget view.
- getSnappedRectRelativeToDragLayer(sTmpRect);
- if (mLeftBorderActive) {
- lp.width = sTmpRect.width() + sTmpRect.left - lp.x;
- }
- if (mTopBorderActive) {
- lp.height = sTmpRect.height() + sTmpRect.top - lp.y;
- }
- if (mRightBorderActive) {
- lp.x = sTmpRect.left;
- }
- if (mBottomBorderActive) {
- lp.y = sTmpRect.top;
- }
-
// Handle invalid resize across CellLayouts in the two panel UI.
if (mCellLayout.getParent() instanceof Workspace) {
Workspace<?> workspace = (Workspace<?>) mCellLayout.getParent();
@@ -508,9 +524,13 @@
* Returns the rect of this view when the frame is snapped around the widget, with the bounds
* relative to the {@link DragLayer}.
*/
- private void getSnappedRectRelativeToDragLayer(Rect out) {
+ private void getSnappedRectRelativeToDragLayer(@NonNull Rect out) {
float scale = mWidgetView.getScaleToFit();
- mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
+ if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
+ getViewRectRelativeToDragLayer(out);
+ } else {
+ mDragLayer.getViewRectRelativeToSelf(mWidgetView, out);
+ }
int width = 2 * mBackgroundPadding + Math.round(scale * out.width());
int height = 2 * mBackgroundPadding + Math.round(scale * out.height());
@@ -523,7 +543,41 @@
out.bottom = out.top + height;
}
+ private void getViewRectRelativeToDragLayer(@NonNull Rect out) {
+ int[] afterPos = getViewPosRelativeToDragLayer();
+ out.set(afterPos[0], afterPos[1], afterPos[0] + mWidgetViewNewRect.width(),
+ afterPos[1] + mWidgetViewNewRect.height());
+ }
+
+ /** Returns the relative x and y values of the widget view after the layout transition */
+ private int[] getViewPosRelativeToDragLayer() {
+ mDragLayer.getLocationInWindow(sDragLayerLoc);
+ int x = sDragLayerLoc[0];
+ int y = sDragLayerLoc[1];
+
+ if (mWidgetViewWindowPos == null) {
+ mWidgetViewWindowPos = new int[2];
+ mWidgetView.getLocationInWindow(mWidgetViewWindowPos);
+ }
+
+ int leftOffset = mWidgetViewNewRect.left - mWidgetViewOldRect.left;
+ int topOffset = mWidgetViewNewRect.top - mWidgetViewOldRect.top;
+
+ return new int[] {mWidgetViewWindowPos[0] - x + leftOffset,
+ mWidgetViewWindowPos[1] - y + topOffset};
+ }
+
private void snapToWidget(boolean animate) {
+ // The widget is guaranteed to be attached to the cell layout at this point, thus setting
+ // the transition here
+ if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()
+ && mWidgetView.getLayoutTransition() == null) {
+ final LayoutTransition transition = new LayoutTransition();
+ transition.setDuration(RESIZE_TRANSITION_DURATION_MS);
+ transition.enableTransitionType(LayoutTransition.CHANGING);
+ mWidgetView.setLayoutTransition(transition);
+ }
+
getSnappedRectRelativeToDragLayer(sTmpRect);
int newWidth = sTmpRect.width();
int newHeight = sTmpRect.height();
@@ -585,7 +639,7 @@
updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
/* springLoadedProgress= */ 0f, /* animatorSet= */ set);
}
- set.setDuration(SNAP_DURATION);
+ set.setDuration(SNAP_DURATION_MS);
set.start();
}
@@ -665,6 +719,10 @@
@Override
protected void handleClose(boolean animate) {
+ if (FeatureFlags.ENABLE_WIDGET_TRANSITION_FOR_RESIZING.get()) {
+ mWidgetView.clearCellChildViewPreLayoutListener();
+ mWidgetView.setLayoutTransition(null);
+ }
mDragLayer.removeView(this);
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index ec874b9..55b8fcc 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -1,28 +1,12 @@
package com.android.launcher3;
-import static android.os.Process.myUserHandle;
-
-import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.LoaderTask;
-import com.android.launcher3.model.ModelDbController;
-import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.LauncherWidgetHolder;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -47,131 +31,4 @@
}
}
}
-
- /**
- * Updates the app widgets whose id has changed during the restore process.
- */
- @WorkerThread
- public static void restoreAppWidgetIds(Context context, ModelDbController controller,
- int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- Log.e(TAG, "Skipping widget ID remap as widgets not supported");
- host.deleteHost();
- return;
- }
- if (!RestoreDbTask.isPending(context)) {
- // Someone has already gone through our DB once, probably LoaderTask. Skip any further
- // modifications of the DB.
- Log.e(TAG, "Skipping widget ID remap as DB already in use");
- for (int widgetId : newWidgetIds) {
- Log.d(TAG, "Deleting widgetId: " + widgetId);
- host.deleteAppWidgetId(widgetId);
- }
- return;
- }
-
- final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
-
- Log.d(TAG, "restoreAppWidgetIds: "
- + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString()
- + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString());
-
- // TODO(b/234700507): Remove the logs after the bug is fixed
- logDatabaseWidgetInfo(controller);
-
- for (int i = 0; i < oldWidgetIds.length; i++) {
- Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
-
- final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
- final int state;
- if (LoaderTask.isValidProvider(provider)) {
- // This will ensure that we show 'Click to setup' UI if required.
- state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- } else {
- state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- }
-
- // b/135926478: Work profile widget restore is broken in platform. This forces us to
- // recreate the widget during loading with the correct host provider.
- long mainProfileId = UserCache.INSTANCE.get(context)
- .getSerialNumberForUser(myUserHandle());
- long controllerProfileId = controller.getSerialNumberForUser(myUserHandle());
- String oldWidgetId = Integer.toString(oldWidgetIds[i]);
- final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
- String profileId = Long.toString(mainProfileId);
- final String[] args = new String[] { oldWidgetId, profileId };
- Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId
- + " with controller profile ID=" + controllerProfileId);
- int result = new ContentWriter(context,
- new ContentWriter.CommitParams(controller, where, args))
- .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
- .put(LauncherSettings.Favorites.RESTORED, state)
- .commit();
- if (result == 0) {
- // TODO(b/234700507): Remove the logs after the bug is fixed
- Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in"
- + " the database anymore");
- try (Cursor cursor = controller.getDb().query(
- Favorites.TABLE_NAME,
- new String[]{Favorites.APPWIDGET_ID},
- "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) {
- if (!cursor.moveToFirst()) {
- // The widget no long exists.
- Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
- + oldWidgetId);
- host.deleteAppWidgetId(newWidgetIds[i]);
- }
- }
- }
- }
-
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
- app.getModel().forceReload();
- }
- }
-
- private static void logDatabaseWidgetInfo(ModelDbController controller) {
- try (Cursor cursor = controller.getDb().query(Favorites.TABLE_NAME,
- new String[]{Favorites.APPWIDGET_ID, Favorites.RESTORED, Favorites.PROFILE_ID},
- Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null,
- null, null, null)) {
- IntArray widgetIdList = new IntArray();
- IntArray widgetRestoreList = new IntArray();
- IntArray widgetProfileIdList = new IntArray();
-
- if (cursor.moveToFirst()) {
- final int widgetIdColumnIndex = cursor.getColumnIndex(Favorites.APPWIDGET_ID);
- final int widgetRestoredColumnIndex = cursor.getColumnIndex(Favorites.RESTORED);
- final int widgetProfileIdIndex = cursor.getColumnIndex(Favorites.PROFILE_ID);
- while (!cursor.isAfterLast()) {
- int widgetId = cursor.getInt(widgetIdColumnIndex);
- int widgetRestoredFlag = cursor.getInt(widgetRestoredColumnIndex);
- int widgetProfileId = cursor.getInt(widgetProfileIdIndex);
-
- widgetIdList.add(widgetId);
- widgetRestoreList.add(widgetRestoredFlag);
- widgetProfileIdList.add(widgetProfileId);
- cursor.moveToNext();
- }
- }
-
- StringBuilder builder = new StringBuilder();
- builder.append("[");
- for (int i = 0; i < widgetIdList.size(); i++) {
- builder.append("[")
- .append(widgetIdList.get(i))
- .append(", ")
- .append(widgetRestoreList.get(i))
- .append(", ")
- .append(widgetProfileIdList.get(i))
- .append("]");
- }
- builder.append("]");
- Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
- + builder.toString());
- } catch (Exception ex) {
- Log.e(TAG, "Getting widget ids from the database failed", ex);
- }
- }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index abf84dd..347c7af 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -272,7 +272,7 @@
mDotParams.scale = 0f;
mForceHideDot = false;
setBackground(null);
- if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()
+ if (Flags.enableTwolineAllapps() || FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()
|| FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get()) {
setMaxLines(1);
}
@@ -405,7 +405,8 @@
* Only if actual text can be displayed in two line, the {@code true} value will be effective.
*/
protected boolean shouldUseTwoLine() {
- return (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() && mDisplay == DISPLAY_ALL_APPS)
+ return ((Flags.enableTwolineAllapps() || FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get())
+ && mDisplay == DISPLAY_ALL_APPS)
|| (FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get()
&& mDisplay == DISPLAY_SEARCH_RESULT);
}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 3c90408..8ec5c18 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -74,14 +74,9 @@
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
// If this is a back key, propagate the key back to the listener
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
- if (TextUtils.isEmpty(getText())) {
- hideKeyboard();
- }
- if (mBackKeyListener != null) {
- return mBackKeyListener.onBackKey();
- }
- return false;
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP
+ && mBackKeyListener != null) {
+ return mBackKeyListener.onBackKey();
}
return super.onKeyPreIme(keyCode, event);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 439cc00..606b2c4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -198,8 +198,8 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ActivityTracker;
-import com.android.launcher3.util.CannedAnimationCoordinator;
import com.android.launcher3.util.BackPressHandler;
+import com.android.launcher3.util.CannedAnimationCoordinator;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -330,10 +330,7 @@
private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
- private static final boolean DESKTOP_MODE_1_SUPPORTED =
- "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode", "0"));
-
- private static final boolean DESKTOP_MODE_2_SUPPORTED =
+ private static final boolean DESKTOP_MODE_SUPPORTED =
"1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0"));
@Thunk
@@ -3301,7 +3298,7 @@
}
private void updateDisallowBack() {
- if (DESKTOP_MODE_1_SUPPORTED || DESKTOP_MODE_2_SUPPORTED) {
+ if (DESKTOP_MODE_SUPPORTED) {
// Do not disable back in launcher when prototype behavior is enabled
return;
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index f0fea61..5e7f21b 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -38,6 +38,7 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
@@ -217,6 +218,16 @@
int childLeft = lp.x;
int childTop = lp.y;
+
+ // We want to get the layout position of the widget, but layout() is a final function in
+ // ViewGroup which makes it impossible to be overridden. Overriding onLayout() will have no
+ // effect since it will not be called when the transition is enabled. The only possible
+ // solution here seems to be sending the positions when CellLayout is laying out the views
+ if (child instanceof LauncherAppWidgetHostView widgetView
+ && widgetView.getCellChildViewPreLayoutListener() != null) {
+ widgetView.getCellChildViewPreLayoutListener().notifyBoundChangeOnPreLayout(child,
+ childLeft, childTop, childLeft + lp.width, childTop + lp.height);
+ }
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
if (lp.dropped) {
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 4768773..c6c38fc 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -55,6 +55,7 @@
int y = presenterPos.cellY;
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|| info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ Log.d(TAG, "add predicted icon " + child.getTag().toString() + " to home screen");
int screenId = presenterPos.screenId;
x = getHotseat().getCellXFromOrder(screenId);
y = getHotseat().getCellYFromOrder(screenId);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 542266a..9c4ce46 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.Flags.enableExpandingPauseWorkButton;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
@@ -26,8 +27,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
-import static com.google.android.platform.launcher.aconfig.flags.Flags.enableExpandingPauseWorkButton;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index be0a898..769c787 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -27,6 +27,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
@@ -176,8 +177,10 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
- int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
- : R.layout.all_apps_icon_twoline;
+ int layout =
+ !(Flags.enableTwolineAllapps() || FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get())
+ ? R.layout.all_apps_icon
+ : R.layout.all_apps_icon_twoline;
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
layout, parent, false);
icon.setLongPressTimeoutFactor(1f);
@@ -187,7 +190,7 @@
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height =
mActivityContext.getDeviceProfile().allAppsCellHeightPx;
- if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
+ if (Flags.enableTwolineAllapps() || FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
icon.getLayoutParams().height += mExtraTextHeight;
}
return new ViewHolder(icon);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 21520bf..4285755 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -154,8 +154,6 @@
"Enable the ability to generate monochromatic icons, if it is not provided by the app");
// TODO(Block 8): Clean up flags
- public static final BooleanFlag ENABLE_MATERIAL_U_POPUP = getDebugFlag(270395516,
- "ENABLE_MATERIAL_U_POPUP", ENABLED, "Switch popup UX to use material U");
// TODO(Block 9): Clean up flags
public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = getReleaseFlag(270395134,
@@ -238,6 +236,7 @@
public static final BooleanFlag COLLECT_SEARCH_HISTORY = getReleaseFlag(270391455,
"COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log");
+ // Aconfig migration complete for ENABLE_TWOLINE_ALLAPPS.
public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
"ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d78bfba..53d0efb 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,6 +16,7 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
@@ -627,7 +628,7 @@
Utilities.scaleRectAboutCenter(iconBounds, iconScale);
// If we are animating to the accepting state, animate the dot out.
- mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
+ mDotParams.scale = Math.max(0, mDotScale - mBackground.getAcceptScaleProgress());
mDotParams.dotColor = mBackground.getDotColor();
mDotRenderer.draw(canvas, mDotParams);
}
@@ -801,6 +802,14 @@
}
}
+ @Override
+ public void onHoverChanged(boolean hovered) {
+ super.onHoverChanged(hovered);
+ if (ENABLE_CURSOR_HOVER_STATES.get()) {
+ mBackground.setHovered(hovered);
+ }
+ }
+
/**
* Interface that provides callbacks to a parent ViewGroup that hosts this FolderIcon.
*/
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 406955c..b320ceb 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,6 +16,8 @@
package com.android.launcher3.folder;
+import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@@ -39,6 +41,9 @@
import android.graphics.Shader;
import android.util.Property;
import android.view.View;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
@@ -55,7 +60,10 @@
private static final boolean DRAW_SHADOW = false;
private static final boolean DRAW_STROKE = false;
- private static final int CONSUMPTION_ANIMATION_DURATION = 100;
+ @VisibleForTesting protected static final int CONSUMPTION_ANIMATION_DURATION = 100;
+
+ @VisibleForTesting protected static final float HOVER_SCALE = 1.1f;
+ @VisibleForTesting protected static final int HOVER_ANIMATION_DURATION = 300;
private final PorterDuffXfermode mShadowPorterDuffXfermode
= new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
@@ -86,17 +94,21 @@
public boolean isClipping = true;
// Drawing / animation configurations
- private static final float ACCEPT_SCALE_FACTOR = 1.20f;
+ @VisibleForTesting protected static final float ACCEPT_SCALE_FACTOR = 1.20f;
// Expressed on a scale from 0 to 255.
private static final int BG_OPACITY = 255;
private static final int MAX_BG_OPACITY = 255;
private static final int SHADOW_OPACITY = 40;
- private ValueAnimator mScaleAnimator;
+ @VisibleForTesting protected ValueAnimator mScaleAnimator;
private ObjectAnimator mStrokeAlphaAnimator;
private ObjectAnimator mShadowAnimator;
+ @VisibleForTesting protected boolean mIsAccepting;
+ @VisibleForTesting protected boolean mIsHovered;
+ @VisibleForTesting protected boolean mIsHoveredOrAnimating;
+
private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
@Override
@@ -203,11 +215,11 @@
}
/**
- * Returns the progress of the scale animation, where 0 means the scale is at 1f
- * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
+ * Returns the progress of the scale animation to accept state, where 0 means the scale is at
+ * 1f and 1 means the scale is at ACCEPT_SCALE_FACTOR. Returns 0 when scaled due to hover.
*/
- float getScaleProgress() {
- return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
+ float getAcceptScaleProgress() {
+ return mIsHoveredOrAnimating ? 0 : (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
}
void invalidate() {
@@ -385,60 +397,70 @@
return mDrawingDelegate != null;
}
- private void animateScale(float finalScale, final Runnable onStart, final Runnable onEnd) {
- final float scale0 = mScale;
- final float scale1 = finalScale;
-
+ protected void animateScale(boolean isAccepting, boolean isHovered) {
if (mScaleAnimator != null) {
mScaleAnimator.cancel();
}
- mScaleAnimator = ValueAnimator.ofFloat(0f, 1.0f);
-
- mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float prog = animation.getAnimatedFraction();
- mScale = prog * scale1 + (1 - prog) * scale0;
- invalidate();
+ final float startScale = mScale;
+ final float endScale = isAccepting ? ACCEPT_SCALE_FACTOR : (isHovered ? HOVER_SCALE : 1f);
+ Interpolator interpolator =
+ isAccepting != mIsAccepting ? ACCELERATE_DECELERATE : EMPHASIZED_DECELERATE;
+ int duration = isAccepting != mIsAccepting ? CONSUMPTION_ANIMATION_DURATION
+ : HOVER_ANIMATION_DURATION;
+ mIsAccepting = isAccepting;
+ mIsHovered = isHovered;
+ if (startScale == endScale) {
+ if (!mIsAccepting) {
+ clearDrawingDelegate();
}
+ mIsHoveredOrAnimating = mIsHovered;
+ return;
+ }
+
+
+ mScaleAnimator = ValueAnimator.ofFloat(0f, 1.0f);
+ mScaleAnimator.addUpdateListener(animation -> {
+ float prog = animation.getAnimatedFraction();
+ mScale = prog * endScale + (1 - prog) * startScale;
+ invalidate();
});
mScaleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- if (onStart != null) {
- onStart.run();
+ if (mIsHovered) {
+ mIsHoveredOrAnimating = true;
}
}
@Override
public void onAnimationEnd(Animator animation) {
- if (onEnd != null) {
- onEnd.run();
+ if (!mIsAccepting) {
+ clearDrawingDelegate();
}
+ mIsHoveredOrAnimating = mIsHovered;
mScaleAnimator = null;
}
});
-
- mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
+ mScaleAnimator.setInterpolator(interpolator);
+ mScaleAnimator.setDuration(duration);
mScaleAnimator.start();
}
public void animateToAccept(CellLayout cl, int cellX, int cellY) {
- animateScale(ACCEPT_SCALE_FACTOR, () -> delegateDrawing(cl, cellX, cellY), null);
+ delegateDrawing(cl, cellX, cellY);
+ animateScale(/* isAccepting= */ true, mIsHovered);
}
public void animateToRest() {
- // This can be called multiple times -- we need to make sure the drawing delegate
- // is saved and restored at the beginning of the animation, since cancelling the
- // existing animation can clear the delgate.
- CellLayout cl = mDrawingDelegate;
- int cellX = mDelegateCellX;
- int cellY = mDelegateCellY;
- animateScale(1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
+ animateScale(/* isAccepting= */ false, mIsHovered);
}
public float getStrokeWidth() {
return mStrokeWidth;
}
+
+ protected void setHovered(boolean hovered) {
+ animateScale(mIsAccepting, /* isHovered= */ hovered);
+ }
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 780cb5e..265378c 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -621,9 +621,12 @@
@UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.")
LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233),
- @UiEvent(doc = "User has invoked split to right half with desktop mode app icon")
+ @UiEvent(doc = "User has invoked split to right half from desktop mode.")
LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM(1412),
+ @UiEvent(doc = "User has invoked split to left half from desktop mode.")
+ LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP(1464),
+
@UiEvent(doc = "User has collapsed the work FAB button by scrolling down in the all apps"
+ " work A-Z list.")
LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276),
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index e0f245f..6b08153 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -18,12 +18,9 @@
import static androidx.core.content.ContextCompat.getColorStateList;
-import static com.android.app.animation.Interpolators.ACCELERATED_EASE;
-import static com.android.app.animation.Interpolators.DECELERATED_EASE;
import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -170,7 +167,7 @@
mIterateChildrenTag = getContext().getString(R.string.popup_container_iterate_children);
- if (!ENABLE_MATERIAL_U_POPUP.get() && mActivityContext.canUseMultipleShadesForPopup()) {
+ if (mActivityContext.canUseMultipleShadesForPopup()) {
mColorIds = new int[]{R.color.popup_shade_first, R.color.popup_shade_second,
R.color.popup_shade_third};
} else {
@@ -241,7 +238,6 @@
}
}
- int numVisibleChild = 0;
int numVisibleShortcut = 0;
View lastView = null;
AnimatorSet colorAnimator = new AnimatorSet();
@@ -256,26 +252,13 @@
MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
mlp.bottomMargin = 0;
- if (colors != null) {
- if (!ENABLE_MATERIAL_U_POPUP.get()) {
- backgroundColor = colors[numVisibleChild % colors.length];
- }
-
- if (ENABLE_MATERIAL_U_POPUP.get() && isShortcutContainer(view)) {
- setChildColor(view, colors[0], colorAnimator);
- mArrowColor = colors[0];
- }
- }
-
- // Arrow color matches the first child or the last child.
- if (!ENABLE_MATERIAL_U_POPUP.get()
- && (mIsAboveIcon || (numVisibleChild == 0 && viewGroup == this))) {
- mArrowColor = backgroundColor;
+ if (colors != null && isShortcutContainer(view)) {
+ setChildColor(view, colors[0], colorAnimator);
+ mArrowColor = colors[0];
}
if (view instanceof ViewGroup && isShortcutContainer(view)) {
assignMarginsAndBackgrounds((ViewGroup) view, backgroundColor);
- numVisibleChild++;
continue;
}
@@ -295,7 +278,6 @@
}
setChildColor(view, backgroundColor, colorAnimator);
- numVisibleChild++;
}
}
@@ -573,23 +555,14 @@
protected void animateOpen() {
setVisibility(View.VISIBLE);
- mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get()
- ? getMaterialUOpenCloseAnimator(
+ mOpenCloseAnimator = getOpenCloseAnimator(
true,
OPEN_DURATION_U,
OPEN_FADE_START_DELAY_U,
OPEN_FADE_DURATION_U,
OPEN_CHILD_FADE_START_DELAY_U,
OPEN_CHILD_FADE_DURATION_U,
- EMPHASIZED_DECELERATE)
- : getOpenCloseAnimator(
- true,
- mOpenDuration,
- mOpenFadeStartDelay,
- mOpenFadeDuration,
- mOpenChildFadeStartDelay,
- mOpenChildFadeDuration,
- DECELERATED_EASE);
+ EMPHASIZED_DECELERATE);
onCreateOpenAnimation(mOpenCloseAnimator);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -603,44 +576,6 @@
mOpenCloseAnimator.start();
}
- private AnimatorSet getOpenCloseAnimator(boolean isOpening, int totalDuration,
- int fadeStartDelay, int fadeDuration, int childFadeStartDelay,
- int childFadeDuration, Interpolator interpolator) {
- final AnimatorSet animatorSet = new AnimatorSet();
- float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
- float[] scaleValues = isOpening ? new float[] {0.5f, 1} : new float[] {1, 0.5f};
-
- ValueAnimator fade = ValueAnimator.ofFloat(alphaValues);
- fade.setStartDelay(fadeStartDelay);
- fade.setDuration(fadeDuration);
- fade.setInterpolator(LINEAR);
- fade.addUpdateListener(anim -> {
- float alpha = (float) anim.getAnimatedValue();
- mArrow.setAlpha(alpha);
- setAlpha(alpha);
- });
- animatorSet.play(fade);
-
- setPivotX(mIsLeftAligned ? 0 : getMeasuredWidth());
- setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0);
- Animator scale = ObjectAnimator.ofFloat(this, View.SCALE_Y, scaleValues);
- scale.setDuration(totalDuration);
- scale.setInterpolator(interpolator);
- animatorSet.play(scale);
-
- if (shouldScaleArrow) {
- Animator arrowScaleAnimator = ObjectAnimator.ofFloat(mArrow, View.SCALE_Y,
- scaleValues);
- arrowScaleAnimator.setDuration(totalDuration);
- arrowScaleAnimator.setInterpolator(interpolator);
- animatorSet.play(arrowScaleAnimator);
- }
-
- fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
-
- return animatorSet;
- }
-
private void fadeInChildViews(ViewGroup group, float[] alphaValues, long startDelay,
long duration, AnimatorSet out) {
for (int i = group.getChildCount() - 1; i >= 0; --i) {
@@ -673,22 +608,14 @@
}
mIsOpen = false;
- mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get()
- ? getMaterialUOpenCloseAnimator(
+ mOpenCloseAnimator = getOpenCloseAnimator(
false,
CLOSE_DURATION_U,
CLOSE_FADE_START_DELAY_U,
CLOSE_FADE_DURATION_U,
CLOSE_CHILD_FADE_START_DELAY_U,
CLOSE_CHILD_FADE_DURATION_U,
- EMPHASIZED_ACCELERATE)
- : getOpenCloseAnimator(false,
- mCloseDuration,
- mCloseFadeStartDelay,
- mCloseFadeDuration,
- mCloseChildFadeStartDelay,
- mCloseChildFadeDuration,
- ACCELERATED_EASE);
+ EMPHASIZED_ACCELERATE);
onCreateCloseAnimation(mOpenCloseAnimator);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@@ -705,7 +632,7 @@
mOpenCloseAnimator.start();
}
- protected AnimatorSet getMaterialUOpenCloseAnimator(boolean isOpening, int scaleDuration,
+ protected AnimatorSet getOpenCloseAnimator(boolean isOpening, int scaleDuration,
int fadeStartDelay, int fadeDuration, int childFadeStartDelay, int childFadeDuration,
Interpolator interpolator) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 1f26bab..934d43b 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -20,21 +20,15 @@
import static com.android.launcher3.Utilities.ATLEAST_P;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
-import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static java.util.Collections.emptyList;
-
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
-import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
@@ -55,17 +49,12 @@
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
-import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.notification.NotificationContainer;
-import com.android.launcher3.notification.NotificationInfo;
-import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.touch.ItemLongClickListener;
@@ -81,7 +70,7 @@
import java.util.stream.Collectors;
/**
- * A container for shortcuts to deep links and notifications associated with an app.
+ * A container for shortcuts to deep links associated with an app.
*
* @param <T> The activity on with the popup shows
*/
@@ -98,8 +87,6 @@
private final float mShortcutHeight;
private BubbleTextView mOriginalIcon;
- private int mNumNotifications;
- private NotificationContainer mNotificationContainer;
private int mContainerWidth;
private ViewGroup mWidgetContainer;
@@ -142,24 +129,12 @@
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mInterceptTouchDown.set(ev.getX(), ev.getY());
}
- if (mNotificationContainer != null
- && mNotificationContainer.onInterceptSwipeEvent(ev)) {
- return true;
- }
// Stop sending touch events to deep shortcut views if user moved beyond touch slop.
return squaredHypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
> squaredTouchSlop(getContext());
}
@Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mNotificationContainer != null) {
- return mNotificationContainer.onSwipeEvent(ev) || super.onTouchEvent(ev);
- }
- return super.onTouchEvent(ev);
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ACTION_POPUP) != 0;
}
@@ -194,14 +169,6 @@
return false;
}
- @Override
- protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
- super.setChildColor(view, color, animatorSetOut);
- if (view.getId() == R.id.notification_container && mNotificationContainer != null) {
- mNotificationContainer.updateBackgroundColor(color, animatorSetOut);
- }
- }
-
/**
* Returns true if we can show the container.
*
@@ -213,7 +180,8 @@
}
/**
- * Shows the notifications and deep shortcuts associated with a Launcher {@param icon}.
+ * Shows a popup with shortcuts associated with a Launcher icon
+ * @param icon the app icon to show the popup for
* @return the container if shown or null.
*/
public static PopupContainerWithArrow<Launcher> showForIcon(BubbleTextView icon) {
@@ -235,21 +203,10 @@
.map(s -> s.getShortcut(launcher, item, icon))
.filter(Objects::nonNull)
.collect(Collectors.toList());
- if (ENABLE_MATERIAL_U_POPUP.get()) {
- container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
- R.layout.popup_container_material_u, launcher.getDragLayer(), false);
- container.configureForLauncher(launcher);
- container.populateAndShowRowsMaterialU(icon, deepShortcutCount, systemShortcuts);
- } else {
- container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
- R.layout.popup_container, launcher.getDragLayer(), false);
- container.configureForLauncher(launcher);
- container.populateAndShow(
- icon,
- deepShortcutCount,
- popupDataProvider.getNotificationKeysForItem(item),
- systemShortcuts);
- }
+ container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
+ R.layout.popup_container, launcher.getDragLayer(), false);
+ container.configureForLauncher(launcher);
+ container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
container.requestFocus();
return container;
@@ -263,91 +220,6 @@
launcher.getDragController().addDragListener(this);
}
- 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) {
- mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_rows_container,
- this, 0);
- initializeSystemShortcut(R.layout.system_shortcut, mSystemShortcutContainer,
- shortcuts.get(0), false);
- return;
- }
- addSystemShortcutsIconsOnly(shortcuts);
- }
-
- @TargetApi(Build.VERSION_CODES.P)
- public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
- 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);
-
- // Add views
- if (mNumNotifications > 0) {
- // Add notification entries
- if (mNotificationContainer == null) {
- mNotificationContainer = findViewById(R.id.notification_container);
- mNotificationContainer.setVisibility(VISIBLE);
- mNotificationContainer.setPopupView(this);
- } else {
- mNotificationContainer.setVisibility(GONE);
- }
- updateNotificationHeader();
- }
- mSystemShortcutContainer = this;
- if (mDeepShortcutContainer == null) {
- mDeepShortcutContainer = findViewById(R.id.deep_shortcuts_container);
- }
- if (hasDeepShortcuts) {
- List<SystemShortcut> systemShortcuts = getNonWidgetSystemShortcuts(shortcuts);
- // 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--) {
- DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, mDeepShortcutContainer);
- v.getLayoutParams().width = mContainerWidth;
- mDeepShortcuts.add(v);
- }
- updateHiddenShortcuts();
- Optional<SystemShortcut.Widgets> widgetShortcutOpt = getWidgetShortcut(shortcuts);
- if (widgetShortcutOpt.isPresent()) {
- if (mWidgetContainer == null) {
- mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container, this, 0);
- }
- initializeWidgetShortcut(mWidgetContainer, widgetShortcutOpt.get());
- }
-
- initializeSystemShortcuts(systemShortcuts);
- } else {
- mDeepShortcutContainer.setVisibility(View.GONE);
- mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_rows_container,
- this, 0);
- mWidgetContainer = mSystemShortcutContainer;
- if (!shortcuts.isEmpty()) {
- for (int i = 0; i < shortcuts.size(); i++) {
- initializeSystemShortcut(
- R.layout.system_shortcut,
- mSystemShortcutContainer,
- shortcuts.get(i),
- i < shortcuts.size() - 1);
- }
- }
- }
- show();
- loadAppShortcuts((ItemInfo) originalIcon.getTag(), notificationKeys);
- }
-
/**
* Populate and show shortcuts for the Launcher U app shortcut design.
* Will inflate the container and shortcut View instances for the popup container.
@@ -355,28 +227,27 @@
* @param deepShortcutCount Number of DeepShortcutView instances to add to container
* @param systemShortcuts List of SystemShortcuts to add to container
*/
- public void populateAndShowRowsMaterialU(final BubbleTextView originalIcon,
+ public void populateAndShowRows(final BubbleTextView originalIcon,
int deepShortcutCount, List<SystemShortcut> systemShortcuts) {
mOriginalIcon = originalIcon;
mContainerWidth = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_width);
if (deepShortcutCount > 0) {
- addAllShortcutsMaterialU(deepShortcutCount, systemShortcuts);
+ addAllShortcuts(deepShortcutCount, systemShortcuts);
} else if (!systemShortcuts.isEmpty()) {
- addSystemShortcutsMaterialU(systemShortcuts,
- R.layout.system_shortcut_rows_container_material_u,
+ addSystemShortcuts(systemShortcuts,
+ R.layout.system_shortcut_rows_container,
R.layout.system_shortcut);
}
show();
- loadAppShortcuts((ItemInfo) originalIcon.getTag(), /* notificationKeys= */ emptyList());
+ loadAppShortcuts((ItemInfo) originalIcon.getTag());
}
/**
* Animates and loads shortcuts on background thread for this popup container
*/
- private void loadAppShortcuts(ItemInfo originalItemInfo,
- List<NotificationKeyData> notificationKeys) {
+ private void loadAppShortcuts(ItemInfo originalItemInfo) {
if (ATLEAST_P) {
setAccessibilityPaneTitle(getTitleForAccessibility());
@@ -387,7 +258,7 @@
// Load the shortcuts on a background thread and update the container as it animates.
MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
mActivityContext, originalItemInfo, new Handler(Looper.getMainLooper()),
- this, mDeepShortcuts, notificationKeys));
+ this, mDeepShortcuts));
}
/**
@@ -396,16 +267,16 @@
* @param deepShortcutCount number of DeepShortcutView instances
* @param systemShortcuts List of SystemShortcuts
*/
- private void addAllShortcutsMaterialU(int deepShortcutCount,
+ private void addAllShortcuts(int deepShortcutCount,
List<SystemShortcut> systemShortcuts) {
if (deepShortcutCount + systemShortcuts.size() <= SHORTCUT_COLLAPSE_THRESHOLD) {
// add all system shortcuts including widgets shortcut to same container
- addSystemShortcutsMaterialU(systemShortcuts,
- R.layout.system_shortcut_rows_container_material_u,
+ addSystemShortcuts(systemShortcuts,
+ R.layout.system_shortcut_rows_container,
R.layout.system_shortcut);
float currentHeight = (mShortcutHeight * systemShortcuts.size())
+ mChildContainerMargin;
- addDeepShortcutsMaterialU(deepShortcutCount, currentHeight);
+ addDeepShortcuts(deepShortcutCount, currentHeight);
return;
}
@@ -426,7 +297,7 @@
initializeWidgetShortcut(mWidgetContainer, widgetShortcutOpt.get());
currentHeight += mShortcutHeight + mChildContainerMargin;
}
- addDeepShortcutsMaterialU(deepShortcutCount, currentHeight);
+ addDeepShortcuts(deepShortcutCount, currentHeight);
}
/**
@@ -464,7 +335,7 @@
* @param systemShortcutContainerLayout Layout Resource for the Container of shortcut Views
* @param systemShortcutLayout Layout Resource for the individual shortcut Views
*/
- private void addSystemShortcutsMaterialU(List<SystemShortcut> systemShortcuts,
+ private void addSystemShortcuts(List<SystemShortcut> systemShortcuts,
@LayoutRes int systemShortcutContainerLayout, @LayoutRes int systemShortcutLayout) {
if (systemShortcuts.size() == 0) {
@@ -486,9 +357,7 @@
return;
}
- mSystemShortcutContainer = ENABLE_MATERIAL_U_POPUP.get()
- ? inflateAndAdd(R.layout.system_shortcut_icons_container_material_u, this)
- : inflateAndAdd(R.layout.system_shortcut_icons_container, this, 0);
+ mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons_container, this);
for (int i = 0; i < systemShortcuts.size(); i++) {
@LayoutRes int shortcutIconLayout = R.layout.system_shortcut_icon_only;
@@ -513,13 +382,13 @@
* @param deepShortcutCount number of DeepShortcutView instances to add
* @param currentHeight height of popup before adding deep shortcuts
*/
- private void addDeepShortcutsMaterialU(int deepShortcutCount, float currentHeight) {
+ private void addDeepShortcuts(int deepShortcutCount, float currentHeight) {
mDeepShortcutContainer = inflateAndAdd(R.layout.deep_shortcut_container, this);
for (int i = deepShortcutCount; i > 0; i--) {
currentHeight += mShortcutHeight;
// when there is limited vertical screen space, limit total popup rows to fit
if (currentHeight >= mActivityContext.getDeviceProfile().availableHeightPx) break;
- DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut_material_u,
+ DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut,
mDeepShortcutContainer);
v.getLayoutParams().width = mContainerWidth;
mDeepShortcuts.add(v);
@@ -527,10 +396,6 @@
updateHiddenShortcuts();
}
- protected NotificationContainer getNotificationContainer() {
- return mNotificationContainer;
- }
-
protected BubbleTextView getOriginalIcon() {
return mOriginalIcon;
}
@@ -548,9 +413,7 @@
}
private String getTitleForAccessibility() {
- return getContext().getString(mNumNotifications == 0 ?
- R.string.action_deep_shortcut :
- R.string.shortcuts_menu_with_notifications_description);
+ return getContext().getString(R.string.action_deep_shortcut);
}
@Override
@@ -564,20 +427,11 @@
: mOriginalIcon.getHeight());
}
- public void applyNotificationInfos(List<NotificationInfo> notificationInfos) {
- if (mNotificationContainer != null) {
- mNotificationContainer.applyNotificationInfos(notificationInfos);
- }
- }
-
protected void updateHiddenShortcuts() {
- int allowedCount = mNotificationContainer != null
- ? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
-
int total = mDeepShortcuts.size();
for (int i = 0; i < total; i++) {
DeepShortcutView view = mDeepShortcuts.get(i);
- view.setVisibility(i >= allowedCount ? GONE : VISIBLE);
+ view.setVisibility(i >= MAX_SHORTCUTS ? GONE : VISIBLE);
}
}
@@ -666,14 +520,6 @@
};
}
- protected void updateNotificationHeader() {
- ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
- DotInfo dotInfo = mActivityContext.getDotInfoForItem(itemInfo);
- if (mNotificationContainer != null && dotInfo != null) {
- mNotificationContainer.updateHeader(dotInfo.getNotificationCount());
- }
- }
-
@Override
public void onDropCompleted(View target, DragObject d, boolean success) { }
diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
index c5d5452..9d6f2a5 100644
--- a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
@@ -15,22 +15,12 @@
*/
package com.android.launcher3.popup;
-import static android.view.View.GONE;
-
import android.content.Context;
import android.view.View;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.notification.NotificationContainer;
-import com.android.launcher3.notification.NotificationKeyData;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ActivityContext;
-import java.util.Map;
-import java.util.function.Predicate;
-
/**
* Utility class to handle updates while the popup is visible (like widgets and
* notification changes)
@@ -67,40 +57,6 @@
}
}
- /**
- * Updates the notification header if the original icon's dot updated.
- */
- @Override
- public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
- ItemInfo itemInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
- PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
- if (updatedDots.test(packageUser)) {
- mPopupContainerWithArrow.updateNotificationHeader();
- }
- }
-
-
- @Override
- public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
- NotificationContainer notificationContainer =
- mPopupContainerWithArrow.getNotificationContainer();
- if (notificationContainer == null) {
- return;
- }
- ItemInfo originalInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
- DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
- if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
- // No more notifications, remove the notification views and expand all shortcuts.
- notificationContainer.setVisibility(GONE);
- mPopupContainerWithArrow.updateHiddenShortcuts();
- mPopupContainerWithArrow.assignMarginsAndBackgrounds(mPopupContainerWithArrow);
- mPopupContainerWithArrow.updateArrowColor();
- } else {
- notificationContainer.trimNotifications(
- NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
- }
- }
-
@Override
public void onSystemShortcutsUpdated() {
mPopupContainerWithArrow.close(true);
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 8be4e6c..aa24f60 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -24,26 +24,19 @@
import android.os.Handler;
import android.os.UserHandle;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.notification.NotificationInfo;
-import com.android.launcher3.notification.NotificationKeyData;
-import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
-import java.util.Iterator;
import java.util.List;
-import java.util.stream.Collectors;
/**
* Contains logic relevant to populating a {@link PopupContainerWithArrow}. In particular,
@@ -52,24 +45,20 @@
public class PopupPopulator {
public static final int MAX_SHORTCUTS = 4;
- @VisibleForTesting static final int NUM_DYNAMIC = 2;
- public static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
+ @VisibleForTesting
+ static final int NUM_DYNAMIC = 2;
/**
* Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
*/
- private static final Comparator<ShortcutInfo> SHORTCUT_RANK_COMPARATOR
- = new Comparator<ShortcutInfo>() {
- @Override
- public int compare(ShortcutInfo a, ShortcutInfo b) {
- if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
- return -1;
- }
- if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
- return 1;
- }
- return Integer.compare(a.getRank(), b.getRank());
+ private static final Comparator<ShortcutInfo> SHORTCUT_RANK_COMPARATOR = (a, b) -> {
+ if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
+ return -1;
}
+ if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
+ return 1;
+ }
+ return Integer.compare(a.getRank(), b.getRank());
};
/**
@@ -77,23 +66,10 @@
* We want the filter to include both static and dynamic shortcuts, so we always
* include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
*
- * @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
* @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
*/
- public static List<ShortcutInfo> sortAndFilterShortcuts(
- List<ShortcutInfo> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
- // Remove up to one specific shortcut before sorting and doing somewhat fancy filtering.
- if (shortcutIdToRemoveFirst != null) {
- Iterator<ShortcutInfo> shortcutIterator = shortcuts.iterator();
- while (shortcutIterator.hasNext()) {
- if (shortcutIterator.next().getId().equals(shortcutIdToRemoveFirst)) {
- shortcutIterator.remove();
- break;
- }
- }
- }
-
- Collections.sort(shortcuts, SHORTCUT_RANK_COMPARATOR);
+ public static List<ShortcutInfo> sortAndFilterShortcuts(List<ShortcutInfo> shortcuts) {
+ shortcuts.sort(SHORTCUT_RANK_COMPARATOR);
if (shortcuts.size() <= MAX_SHORTCUTS) {
return shortcuts;
}
@@ -127,37 +103,20 @@
}
/**
- * Returns a runnable to update the provided shortcuts and notifications
+ * Returns a runnable to update the provided shortcuts
*/
public static <T extends Context & ActivityContext> Runnable createUpdateRunnable(
final T context,
final ItemInfo originalInfo,
final Handler uiHandler, final PopupContainerWithArrow container,
- final List<DeepShortcutView> shortcutViews,
- final List<NotificationKeyData> notificationKeys) {
+ final List<DeepShortcutView> shortcutViews) {
final ComponentName activity = originalInfo.getTargetComponent();
final UserHandle user = originalInfo.user;
return () -> {
- if (!notificationKeys.isEmpty()) {
- NotificationListener notificationListener =
- NotificationListener.getInstanceIfConnected();
- final List<NotificationInfo> infos;
- if (notificationListener == null) {
- infos = Collections.emptyList();
- } else {
- infos = notificationListener.getNotificationsForKeys(notificationKeys).stream()
- .map(sbn -> new NotificationInfo(context, sbn, originalInfo))
- .collect(Collectors.toList());
- }
- uiHandler.post(() -> container.applyNotificationInfos(infos));
- }
-
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, user)
.withContainer(activity)
.query(ShortcutRequest.PUBLISHED);
- String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
- : notificationKeys.get(0).shortcutId;
- shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
+ shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts);
IconCache cache = LauncherAppState.getInstance(context).getIconCache();
for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
final ShortcutInfo shortcut = shortcuts.get(i);
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 4725dd1..10005e5 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -28,6 +28,8 @@
import android.app.backup.BackupManager;
import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -42,20 +44,26 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
-import com.android.launcher3.AppWidgetsRestoredReceiver;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
@@ -377,11 +385,13 @@
.putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
}
- private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
+ @WorkerThread
+ @VisibleForTesting
+ void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
- AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller,
+ restoreAppWidgetIds(context, controller,
IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
host);
@@ -392,6 +402,133 @@
lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
}
+ /**
+ * Updates the app widgets whose id has changed during the restore process.
+ */
+ @WorkerThread
+ private void restoreAppWidgetIds(Context context, ModelDbController controller,
+ int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ Log.e(TAG, "Skipping widget ID remap as widgets not supported");
+ host.deleteHost();
+ return;
+ }
+ if (!RestoreDbTask.isPending(context)) {
+ // Someone has already gone through our DB once, probably LoaderTask. Skip any further
+ // modifications of the DB.
+ Log.e(TAG, "Skipping widget ID remap as DB already in use");
+ for (int widgetId : newWidgetIds) {
+ Log.d(TAG, "Deleting widgetId: " + widgetId);
+ host.deleteAppWidgetId(widgetId);
+ }
+ return;
+ }
+
+ final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+
+ Log.d(TAG, "restoreAppWidgetIds: "
+ + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString()
+ + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString());
+
+ // TODO(b/234700507): Remove the logs after the bug is fixed
+ logDatabaseWidgetInfo(controller);
+
+ for (int i = 0; i < oldWidgetIds.length; i++) {
+ Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+
+ final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
+ final int state;
+ if (LoaderTask.isValidProvider(provider)) {
+ // This will ensure that we show 'Click to setup' UI if required.
+ state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ } else {
+ state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ }
+
+ // b/135926478: Work profile widget restore is broken in platform. This forces us to
+ // recreate the widget during loading with the correct host provider.
+ long mainProfileId = UserCache.INSTANCE.get(context)
+ .getSerialNumberForUser(myUserHandle());
+ long controllerProfileId = controller.getSerialNumberForUser(myUserHandle());
+ String oldWidgetId = Integer.toString(oldWidgetIds[i]);
+ final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
+ String profileId = Long.toString(mainProfileId);
+ final String[] args = new String[] { oldWidgetId, profileId };
+ Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId
+ + " with controller profile ID=" + controllerProfileId);
+ int result = new ContentWriter(context,
+ new ContentWriter.CommitParams(controller, where, args))
+ .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+ .put(LauncherSettings.Favorites.RESTORED, state)
+ .commit();
+ if (result == 0) {
+ // TODO(b/234700507): Remove the logs after the bug is fixed
+ Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in"
+ + " the database anymore");
+ try (Cursor cursor = controller.getDb().query(
+ Favorites.TABLE_NAME,
+ new String[]{Favorites.APPWIDGET_ID},
+ "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) {
+ if (!cursor.moveToFirst()) {
+ // The widget no long exists.
+ Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
+ + oldWidgetId);
+ host.deleteAppWidgetId(newWidgetIds[i]);
+ }
+ }
+ }
+ }
+
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app != null) {
+ app.getModel().forceReload();
+ }
+ }
+
+ private static void logDatabaseWidgetInfo(ModelDbController controller) {
+ try (Cursor cursor = controller.getDb().query(Favorites.TABLE_NAME,
+ new String[]{Favorites.APPWIDGET_ID, Favorites.RESTORED, Favorites.PROFILE_ID},
+ Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null,
+ null, null, null)) {
+ IntArray widgetIdList = new IntArray();
+ IntArray widgetRestoreList = new IntArray();
+ IntArray widgetProfileIdList = new IntArray();
+
+ if (cursor.moveToFirst()) {
+ final int widgetIdColumnIndex = cursor.getColumnIndex(Favorites.APPWIDGET_ID);
+ final int widgetRestoredColumnIndex = cursor.getColumnIndex(Favorites.RESTORED);
+ final int widgetProfileIdIndex = cursor.getColumnIndex(Favorites.PROFILE_ID);
+ while (!cursor.isAfterLast()) {
+ int widgetId = cursor.getInt(widgetIdColumnIndex);
+ int widgetRestoredFlag = cursor.getInt(widgetRestoredColumnIndex);
+ int widgetProfileId = cursor.getInt(widgetProfileIdIndex);
+
+ widgetIdList.add(widgetId);
+ widgetRestoreList.add(widgetRestoredFlag);
+ widgetProfileIdList.add(widgetProfileId);
+ cursor.moveToNext();
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ for (int i = 0; i < widgetIdList.size(); i++) {
+ builder.append("[")
+ .append(widgetIdList.get(i))
+ .append(", ")
+ .append(widgetRestoreList.get(i))
+ .append(", ")
+ .append(widgetProfileIdList.get(i))
+ .append("]");
+ }
+ builder.append("]");
+ Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
+ + builder.toString());
+ } catch (Exception ex) {
+ Log.e(TAG, "Getting widget ids from the database failed", ex);
+ }
+ }
+
public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
@NonNull int[] newIds) {
LauncherPrefs.get(context).putSync(
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index e8be12c..6e697d9 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -18,7 +18,6 @@
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import android.content.Context;
@@ -45,7 +44,6 @@
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -203,20 +201,10 @@
}
int deepShortcutCount = popupDataProvider.getShortcutCountForItem(item);
final PopupContainerWithArrow<SecondaryDisplayLauncher> container;
- if (ENABLE_MATERIAL_U_POPUP.get()) {
- container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
- R.layout.popup_container_material_u, mActivity.getDragLayer(), false);
- container.populateAndShowRowsMaterialU((BubbleTextView) v, deepShortcutCount,
- systemShortcuts);
- } else {
- container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
- R.layout.popup_container, mActivity.getDragLayer(), false);
- container.populateAndShow(
- (BubbleTextView) v,
- deepShortcutCount,
- Collections.emptyList(),
- systemShortcuts);
- }
+ container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
+ R.layout.popup_container, mActivity.getDragLayer(), false);
+ container.populateAndShowRows((BubbleTextView) v, deepShortcutCount,
+ systemShortcuts);
container.requestFocus();
if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index f95548d..3db2ddf 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -27,26 +27,29 @@
import java.util.function.BiConsumer;
public final class TestLogging {
+ private static final String TAPL_EVENTS_TAG = "TaplEvents";
+ private static final String LAUNCHER_EVENTS_TAG = "LauncherEvents";
private static BiConsumer<String, String> sEventConsumer;
public static boolean sHadEventsNotFromTest;
- private static void recordEventSlow(String sequence, String event) {
- Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
+ private static void recordEventSlow(String sequence, String event, boolean reportToTapl) {
+ Log.d(reportToTapl ? TAPL_EVENTS_TAG : LAUNCHER_EVENTS_TAG,
+ sequence + " / " + event);
final BiConsumer<String, String> eventConsumer = sEventConsumer;
- if (eventConsumer != null) {
+ if (reportToTapl && eventConsumer != null) {
eventConsumer.accept(sequence, event);
}
}
public static void recordEvent(String sequence, String event) {
if (Utilities.isRunningInTestHarness()) {
- recordEventSlow(sequence, event);
+ recordEventSlow(sequence, event, true);
}
}
public static void recordEvent(String sequence, String message, Object parameter) {
if (Utilities.isRunningInTestHarness()) {
- recordEventSlow(sequence, message + ": " + parameter);
+ recordEventSlow(sequence, message + ": " + parameter, true);
}
}
@@ -59,14 +62,23 @@
public static void recordKeyEvent(String sequence, String message, KeyEvent event) {
if (Utilities.isRunningInTestHarness()) {
- recordEventSlow(sequence, message + ": " + event);
+ recordEventSlow(sequence, message + ": " + event, true);
registerEventNotFromTest(event);
}
}
public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
- if (Utilities.isRunningInTestHarness() && event.getAction() != MotionEvent.ACTION_MOVE) {
- recordEventSlow(sequence, message + ": " + event);
+ final int action = event.getAction();
+ if (Utilities.isRunningInTestHarness() && action != MotionEvent.ACTION_MOVE) {
+ // "Expecting" in TAPL ACTION_DOWN, UP and CANCEL events was thought to be producing
+ // considerable noise in tests due to failed checks for expected events. So we are not
+ // sending them to TAPL.
+ // Other events, such as EVENT_PILFER_POINTERS produce less noise and are thought to
+ // be more useful.
+ final boolean reportToTapl = action != MotionEvent.ACTION_DOWN
+ && action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL;
+ recordEventSlow(sequence, message + ": " + event, reportToTapl);
registerEventNotFromTest(event);
}
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index b62f60d..cf59085 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -18,7 +18,6 @@
import static androidx.core.content.ContextCompat.getColorStateList;
import static com.android.launcher3.LauncherState.EDIT_MODE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
@@ -26,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -149,12 +147,8 @@
@Override
public void assignMarginsAndBackgrounds(ViewGroup viewGroup) {
- if (ENABLE_MATERIAL_U_POPUP.get()) {
- assignMarginsAndBackgrounds(viewGroup,
- getColorStateList(getContext(), mColorIds[0]).getDefaultColor());
- } else {
- assignMarginsAndBackgrounds(viewGroup, Color.TRANSPARENT);
- }
+ assignMarginsAndBackgrounds(viewGroup,
+ getColorStateList(getContext(), mColorIds[0]).getDefaultColor());
}
public static <T extends Context & ActivityContext> OptionsPopupView<T> show(
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 98d854e..76a044d 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -37,6 +37,7 @@
import android.widget.Advanceable;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.CheckLongPressHelper;
@@ -63,6 +64,8 @@
private static final long ADVANCE_INTERVAL = 20000;
private static final long ADVANCE_STAGGER = 250;
+ private @Nullable CellChildViewPreLayoutListener mCellChildViewPreLayoutListener;
+
// Maintains a list of widget ids which are supposed to be auto advanced.
private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
// Maximum duration for which updates can be deferred.
@@ -335,6 +338,26 @@
requestLayout();
}
+ /**
+ * Set the pre-layout listener
+ * @param listener The listener to be notified when {@code CellLayout} is to layout this view
+ */
+ public void setCellChildViewPreLayoutListener(
+ @NonNull CellChildViewPreLayoutListener listener) {
+ mCellChildViewPreLayoutListener = listener;
+ }
+
+ /** @return The current cell layout listener */
+ @Nullable
+ public CellChildViewPreLayoutListener getCellChildViewPreLayoutListener() {
+ return mCellChildViewPreLayoutListener;
+ }
+
+ /** Clear the listener for the pre-layout in CellLayout */
+ public void clearCellChildViewPreLayoutListener() {
+ mCellChildViewPreLayoutListener = null;
+ }
+
@Override
public void onColorsChanged(SparseIntArray colors) {
if (isDeferringUpdates()) {
@@ -460,4 +483,19 @@
}
return false;
}
+
+ /**
+ * Listener interface to be called when {@code CellLayout} is about to layout this child view
+ */
+ public interface CellChildViewPreLayoutListener {
+ /**
+ * Notify the bound changes to this view on pre-layout
+ * @param v The view which the listener is set for
+ * @param left The new left coordinate of this view
+ * @param top The new top coordinate of this view
+ * @param right The new right coordinate of this view
+ * @param bottom The new bottom coordinate of this view
+ */
+ void notifyBoundChangeOnPreLayout(View v, int left, int top, int right, int bottom);
+ }
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index abca1f8..a4b605c 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -794,13 +794,15 @@
}
// Checks the orientation of the screen
- if (LARGE_SCREEN_WIDGET_PICKER.get()
- && mOrientation != newConfig.orientation
- && mDeviceProfile.isTablet
- && !mDeviceProfile.isTwoPanels) {
+ if (mOrientation != newConfig.orientation) {
mOrientation = newConfig.orientation;
- handleClose(false);
- show(Launcher.getLauncher(getContext()), false);
+ if (LARGE_SCREEN_WIDGET_PICKER.get()
+ && mDeviceProfile.isTablet && !mDeviceProfile.isTwoPanels) {
+ handleClose(false);
+ show(Launcher.getLauncher(getContext()), false);
+ } else {
+ reset();
+ }
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 89ebe8c..d40efce 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -88,7 +88,7 @@
"truth-prebuilt",
"platform-test-rules",
"testables",
- "launcher_flags_lib_test",
+ "com_android_launcher3_flags_lib",
],
manifest: "AndroidManifest-common.xml",
platform_apis: true,
@@ -107,7 +107,7 @@
],
static_libs: [
"Launcher3TestLib",
- "launcher_flags_lib_test",
+ "com_android_launcher3_flags_lib",
],
libs: [
"android.test.base",
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index bb61fbe..ee151bb 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -345,6 +345,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity-alias android:name="WebSearchActivity"
+ android:label="WebSearchActivity"
+ android:exported="true"
+ android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.WEB_SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity-alias>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
<provider
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 87ec260..cdf8f08 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -39,7 +39,6 @@
public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8;
public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9;
public static final int EDIT_MODE_STATE_ORDINAL = 10;
- public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static final String SEQUENCE_MAIN = "Main";
public static final String SEQUENCE_TIS = "TIS";
public static final String SEQUENCE_PILFER = "Pilfer";
@@ -155,13 +154,11 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528";
- public static final String FLAKY_ACTIVITY_COUNT = "b/260260325";
public static final String FLAKY_QUICK_SWITCH_TO_PREVIOUS_APP = "b/286084688";
public static final String ICON_MISSING = "b/282963545";
- public static final String LAUNCH_SPLIT_PAIR = "b/288939273";
-
public static final String OVERVIEW_OVER_HOME = "b/279059025";
public static final String INCORRECT_HOME_STATE = "b/293191790";
+
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
public static final String REQUEST_IS_EMULATE_DISPLAY_RUNNING = "is-emulate-display-running";
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index f34a29e..032a7b4 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -27,7 +27,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import org.junit.After;
import org.junit.Test;
@@ -74,7 +73,6 @@
}
@Test
- @ScreenRecord // b/202985412
public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
final ItemOperator findPromiseApp = (info, view) ->
@@ -97,7 +95,6 @@
}
@Test
- @ScreenRecord // b/202985412
public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
final ItemOperator findPromiseApp = (info, view) ->
diff --git a/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java b/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
new file mode 100644
index 0000000..715a1f8
--- /dev/null
+++ b/tests/src/com/android/launcher3/folder/PreviewBackgroundTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import static com.android.launcher3.folder.PreviewBackground.ACCEPT_SCALE_FACTOR;
+import static com.android.launcher3.folder.PreviewBackground.CONSUMPTION_ANIMATION_DURATION;
+import static com.android.launcher3.folder.PreviewBackground.HOVER_ANIMATION_DURATION;
+import static com.android.launcher3.folder.PreviewBackground.HOVER_SCALE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.PathInterpolator;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.CellLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PreviewBackgroundTest {
+
+ private static final float REST_SCALE = 1f;
+ private static final float EPSILON = 0.00001f;
+
+ @Mock
+ CellLayout mCellLayout;
+
+ private final PreviewBackground mPreviewBackground = new PreviewBackground();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mPreviewBackground.mScale = REST_SCALE;
+ mPreviewBackground.mIsAccepting = false;
+ mPreviewBackground.mIsHovered = false;
+ mPreviewBackground.mIsHoveredOrAnimating = false;
+ mPreviewBackground.invalidate();
+ }
+
+ @Test
+ public void testAnimateScale_restToHovered() {
+ mPreviewBackground.setHovered(true);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ HOVER_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_restToNotHovered() {
+ mPreviewBackground.setHovered(false);
+
+ assertEquals("Scale changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertNull("Animator not null.", mPreviewBackground.mScaleAnimator);
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_hoveredToHovered() {
+ mPreviewBackground.mScale = HOVER_SCALE;
+ mPreviewBackground.mIsHovered = true;
+ mPreviewBackground.mIsHoveredOrAnimating = true;
+ mPreviewBackground.invalidate();
+
+ mPreviewBackground.setHovered(true);
+
+ assertEquals("Scale changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ assertNull("Animator not null.", mPreviewBackground.mScaleAnimator);
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_hoveredToRest() {
+ mPreviewBackground.mScale = HOVER_SCALE;
+ mPreviewBackground.mIsHovered = true;
+ mPreviewBackground.mIsHoveredOrAnimating = true;
+ mPreviewBackground.invalidate();
+
+ mPreviewBackground.setHovered(false);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ HOVER_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_restToAccept() {
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ CONSUMPTION_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator()
+ instanceof AccelerateDecelerateInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_restToRest() {
+ mPreviewBackground.animateToRest();
+
+ assertEquals("Scale changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertNull("Animator not null.", mPreviewBackground.mScaleAnimator);
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_acceptToRest() {
+ mPreviewBackground.mScale = ACCEPT_SCALE_FACTOR;
+ mPreviewBackground.mIsAccepting = true;
+ mPreviewBackground.invalidate();
+
+ mPreviewBackground.animateToRest();
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ CONSUMPTION_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator()
+ instanceof AccelerateDecelerateInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_acceptToHover() {
+ mPreviewBackground.mScale = ACCEPT_SCALE_FACTOR;
+ mPreviewBackground.mIsAccepting = true;
+ mPreviewBackground.invalidate();
+
+ mPreviewBackground.mIsAccepting = false;
+ mPreviewBackground.setHovered(true);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ HOVER_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_hoverToAccept() {
+ mPreviewBackground.mScale = HOVER_SCALE;
+ mPreviewBackground.mIsHovered = true;
+ mPreviewBackground.mIsHoveredOrAnimating = true;
+ mPreviewBackground.invalidate();
+
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ CONSUMPTION_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator()
+ instanceof AccelerateDecelerateInterpolator);
+ mPreviewBackground.mIsHovered = false;
+ endAnimation();
+ assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_midwayToHoverToAccept() {
+ mPreviewBackground.setHovered(true);
+ runAnimationToFraction(0.5f);
+ assertTrue("Scale not changed.",
+ mPreviewBackground.mScale > REST_SCALE && mPreviewBackground.mScale < HOVER_SCALE);
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, ACCEPT_SCALE_FACTOR, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ CONSUMPTION_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator()
+ instanceof AccelerateDecelerateInterpolator);
+ mPreviewBackground.mIsHovered = false;
+ endAnimation();
+ assertEquals("Scale progress not 1.", mPreviewBackground.getAcceptScaleProgress(), 1,
+ EPSILON);
+ assertNull("Animator not null.", mPreviewBackground.mScaleAnimator);
+ }
+
+ @Test
+ public void testAnimateScale_partWayToAcceptToHover() {
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(0.25f);
+ assertTrue("Scale not changed part way.", mPreviewBackground.mScale > REST_SCALE
+ && mPreviewBackground.mScale < ACCEPT_SCALE_FACTOR);
+
+ mPreviewBackground.mIsAccepting = false;
+ mPreviewBackground.setHovered(true);
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ HOVER_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_midwayToAcceptEqualsHover() {
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(0.5f);
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ mPreviewBackground.mIsAccepting = false;
+
+ mPreviewBackground.setHovered(true);
+
+ assertEquals("Scale changed.", mPreviewBackground.mScale, HOVER_SCALE, EPSILON);
+ assertNull("Animator not null.", mPreviewBackground.mScaleAnimator);
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_midwayToHoverToRest() {
+ mPreviewBackground.setHovered(true);
+ runAnimationToFraction(0.5f);
+ assertTrue("Scale not changed midway.",
+ mPreviewBackground.mScale > REST_SCALE && mPreviewBackground.mScale < HOVER_SCALE);
+
+ mPreviewBackground.mIsHovered = false;
+ mPreviewBackground.animateToRest();
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ HOVER_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator() instanceof PathInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ @Test
+ public void testAnimateScale_midwayToAcceptToRest() {
+ mPreviewBackground.animateToAccept(mCellLayout, 0, 0);
+ runAnimationToFraction(0.5f);
+ assertTrue("Scale not changed.", mPreviewBackground.mScale > REST_SCALE
+ && mPreviewBackground.mScale < ACCEPT_SCALE_FACTOR);
+
+ mPreviewBackground.animateToRest();
+ runAnimationToFraction(1f);
+
+ assertEquals("Scale not changed.", mPreviewBackground.mScale, REST_SCALE, EPSILON);
+ assertEquals("Duration not correct.", mPreviewBackground.mScaleAnimator.getDuration(),
+ CONSUMPTION_ANIMATION_DURATION);
+ assertTrue("Wrong interpolator used.",
+ mPreviewBackground.mScaleAnimator.getInterpolator()
+ instanceof AccelerateDecelerateInterpolator);
+ endAnimation();
+ assertEquals("Scale progress not 0.", mPreviewBackground.getAcceptScaleProgress(), 0,
+ EPSILON);
+ }
+
+ private void runAnimationToFraction(float animationFraction) {
+ mPreviewBackground.mScaleAnimator.setCurrentFraction(animationFraction);
+ }
+
+ private void endAnimation() {
+ mPreviewBackground.mScaleAnimator.end();
+ }
+}
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 6764e09..f3eb0a9 100644
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -64,34 +64,14 @@
MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
}
- @Test
- public void testDeDupeShortcutId() {
- // Successfully remove one of the shortcuts
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 2, 0, generateId(true, 1));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 2, generateId(false, 1));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 1, generateId(false, 1));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 1, 2, generateId(true, 1));
- // Successfully keep all shortcuts when id doesn't exist
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(false, 1));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(true, 4));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(false, 4));
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(true, 4));
- }
-
private String generateId(boolean isStatic, int rank) {
return (isStatic ? "static" : "dynamic") + rank;
}
private void filterShortcutsAndAssertNumStaticAndDynamic(
List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) {
- filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
- }
-
- private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts,
- int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
Collections.shuffle(shortcuts);
- List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
- shortcuts, shortcutIdToRemove);
+ List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts);
assertIsSorted(filteredShortcuts);
int numStatic = 0;
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 73bb586..15f6177 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -19,17 +19,30 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
+
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.app.backup.BackupManager;
+import android.appwidget.AppWidgetHost;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -43,6 +56,7 @@
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.ModelDbController;
@@ -52,6 +66,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.stream.IntStream;
/**
* Tests for {@link RestoreDbTask}
@@ -66,11 +84,21 @@
private LauncherModelHelper mModelHelper;
private Context mContext;
+ private RestoreDbTask mTask;
+ private ModelDbController mMockController;
+ private SQLiteDatabase mMockDb;
+ private Cursor mMockCursor;
+ private LauncherPrefs mPrefs;
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
mContext = mModelHelper.sandboxContext;
+ mTask = new RestoreDbTask();
+ mMockController = Mockito.mock(ModelDbController.class);
+ mMockDb = mock(SQLiteDatabase.class);
+ mMockCursor = mock(Cursor.class);
+ mPrefs = new LauncherPrefs(mContext);
}
@After
@@ -148,8 +176,7 @@
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
- RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(mContext, controller, controller.getDb(), bm);
+ mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -178,8 +205,7 @@
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
- RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(mContext, controller, controller.getDb(), bm);
+ mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -188,6 +214,83 @@
assertEquals(10, getCount(db, "select * from favorites"));
}
+ @Test
+ public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() {
+ // When
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+ // Then
+ assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
+ }
+
+ @Test
+ public void givenNoPendingRestore_WhenRestoreAppWidgetIds_ThenRemoveNewWidgetIds() {
+ // Given
+ AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
+ int[] expectedOldIds = generateOldWidgetIds(expectedHost);
+ int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
+ when(mMockController.getDb()).thenReturn(mMockDb);
+ mPrefs.remove(RESTORE_DEVICE);
+
+ // When
+ RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+
+ // Then
+ assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
+ assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
+ verifyZeroInteractions(mMockController);
+ }
+
+ @Test
+ public void givenRestoreWithNonExistingWidgets_WhenRestoreAppWidgetIds_ThenRemoveNewIds() {
+ // Given
+ AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
+ int[] expectedOldIds = generateOldWidgetIds(expectedHost);
+ int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
+ when(mMockController.getDb()).thenReturn(mMockDb);
+ when(mMockDb.query(any(), any(), any(), any(), any(), any(), any())).thenReturn(
+ mMockCursor);
+ when(mMockCursor.moveToFirst()).thenReturn(false);
+ RestoreDbTask.setPending(mContext);
+
+ // When
+ RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+
+ // Then
+ assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
+ assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
+ verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
+ }
+
+ @Test
+ public void givenRestore_WhenRestoreAppWidgetIds_ThenAddNewIds() {
+ // Given
+ AppWidgetHost expectedHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID);
+ int[] expectedOldIds = generateOldWidgetIds(expectedHost);
+ int[] expectedNewIds = generateNewWidgetIds(expectedHost, expectedOldIds);
+ int[] allExpectedIds = IntStream.concat(
+ Arrays.stream(expectedOldIds),
+ Arrays.stream(expectedNewIds)
+ ).toArray();
+
+ when(mMockController.getDb()).thenReturn(mMockDb);
+ when(mMockDb.query(any(), any(), any(), any(), any(), any(), any()))
+ .thenReturn(mMockCursor);
+ when(mMockCursor.moveToFirst()).thenReturn(true);
+ when(mMockCursor.isAfterLast()).thenReturn(true);
+ RestoreDbTask.setPending(mContext);
+
+ // When
+ RestoreDbTask.setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
+ mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
+
+ // Then
+ assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds);
+ assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
+ verify(mMockController, times(expectedOldIds.length)).update(any(), any(), any(), any());
+ }
+
private void addIconsBulk(MyModelDbController controller,
int count, int screen, long profileId) {
int columns = LauncherAppState.getIDP(mContext).numColumns;
@@ -270,6 +373,19 @@
}
}
+ private int[] generateOldWidgetIds(AppWidgetHost host) {
+ // generate some widget ids in case there are none
+ host.allocateAppWidgetId();
+ host.allocateAppWidgetId();
+ return host.getAppWidgetIds();
+ }
+
+ private int[] generateNewWidgetIds(AppWidgetHost host, int[] oldWidgetIds) {
+ // map as many new ids as old ids
+ return Arrays.stream(oldWidgetIds)
+ .map(id -> host.allocateAppWidgetId()).toArray();
+ }
+
private class MyModelDbController extends ModelDbController {
public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
diff --git a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index ba17fdc..bba8c89 100644
--- a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -30,16 +30,21 @@
import android.view.ViewGroup;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.search.StringMatcherUtility;
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.StaticMockitoRule;
import com.android.launcher3.views.BaseDragLayer;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
/**
* Unit tests for testing modifyTitleToSupportMultiLine() in BubbleTextView.java
@@ -50,6 +55,7 @@
*/
public class BubbleTextViewTest {
+ @Rule public StaticMockitoRule mockitoRule = new StaticMockitoRule(Flags.class);
private static final StringMatcherUtility.StringMatcher
MATCHER = StringMatcherUtility.StringMatcher.getInstance();
private static final int ONE_LINE = 1;
@@ -77,6 +83,8 @@
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(Flags.enableTwolineAllapps()).thenReturn(false);
Utilities.enableRunningInTestHarnessForTests();
mContext = new ActivityContextWrapper(getApplicationContext());
mBubbleTextView = new BubbleTextView(mContext);
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index e12cf2d..c462f59 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -158,7 +158,6 @@
}
@Test
- @ScreenRecord
public void testPressHomeOnAllAppsContextMenu() throws Exception {
final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
allApps.freeze();
@@ -261,7 +260,6 @@
@PlatinumTest(focusArea = "launcher")
@Test
- @ScreenRecord // b/202433017
public void testWorkspace() throws Exception {
// Set workspace that includes the chrome Activity app icon on the hotseat.
LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
@@ -556,8 +554,9 @@
@Test
@PortraitLandscape
- @PlatinumTest(focusArea = "launcher")
- @ScreenRecord // TODO(b/293944634): Remove after flaky debug
+ // TODO(b/293944634): Remove Screenrecord after flaky debug, and add
+ // @PlatinumTest(focusArea = "launcher") back
+ @ScreenRecord
public void testUninstallFromWorkspace() throws Exception {
installDummyAppAndWaitForUIUpdate();
try {
@@ -619,10 +618,13 @@
}
}
+ /**
+ * Adds three icons to the workspace and removes one of them by dragging to uninstall.
+ */
@Test
@ScreenRecord // b/241821721
@PlatinumTest(focusArea = "launcher")
- public void getIconsPosition_afterIconRemoved_notContained() throws IOException {
+ public void uninstallWorkspaceIcon() throws IOException {
Point[] gridPositions = getCornersAndCenterPositions();
StringBuilder sb = new StringBuilder();
for (Point p : gridPositions) {
@@ -643,6 +645,10 @@
mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
+ // Debug for b/288944469 I want to test if we are not waiting enough after removing
+ // the icon to request the list of icons again, since the items are not removed
+ // immediately. This should reduce the flake rate
+ SystemClock.sleep(500);
Map<String, Point> finalPositions =
mLauncher.getWorkspace().getWorkspaceIconsPositions();
assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 61cdd17..5b9adcd 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -22,13 +22,10 @@
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-import static com.google.android.platform.launcher.aconfig.flags.Flags.FLAG_ENABLE_EXPANDING_PAUSE_WORK_BUTTON;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import android.view.View;
@@ -47,7 +44,6 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
-import org.junit.Rule;
import org.junit.Test;
import java.util.Objects;
@@ -56,7 +52,6 @@
public class WorkProfileTest extends AbstractLauncherUiTest {
private static final int WORK_PAGE = ActivityAllAppsContainerView.AdapterHolder.WORK;
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private int mProfileUserId;
private boolean mWorkProfileSetupSuccessful;
@@ -65,7 +60,6 @@
@Before
@Override
public void setUp() throws Exception {
- mSetFlagsRule.disableFlags(FLAG_ENABLE_EXPANDING_PAUSE_WORK_BUTTON);
super.setUp();
String output =
mDevice.executeShellCommand(
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
index 49abad4..4b65439 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java
@@ -180,7 +180,8 @@
}
@Override
- String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN, long timestamp) {
+ String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN, long timestamp,
+ int windowSizePx) {
// If the view was previously seen, proceed with analysis only if it was present in the
// view hierarchy in the previous frame.
if (oldInfo != null && oldInfo.frameN != frameN) return null;
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java
index 09e2f65..786791c 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AnomalyDetector.java
@@ -68,17 +68,18 @@
* null value means that the view. 'oldInfo' and 'newInfo' cannot be both null.
* If an anomaly is detected, an exception will be thrown.
*
- * @param oldInfo the view, as seen in the last frame that contained it in the view
- * hierarchy before 'currentFrame'. 'null' means that the view is first seen
- * in the 'currentFrame'.
- * @param newInfo the view in the view hierarchy of the 'currentFrame'. 'null' means that
- * the view is not present in the 'currentFrame', but was present in the previous
- * frame.
- * @param frameN number of the current frame.
+ * @param oldInfo the view, as seen in the last frame that contained it in the view
+ * hierarchy before 'currentFrame'. 'null' means that the view is first seen
+ * in the 'currentFrame'.
+ * @param newInfo the view in the view hierarchy of the 'currentFrame'. 'null' means that
+ * the view is not present in the 'currentFrame', but was present in the
+ * previous frame.
+ * @param frameN number of the current frame.
+ * @param windowSizePx maximum of the window width and height, in pixels.
* @return Anomaly diagnostic message if an anomaly has been detected; null otherwise.
*/
abstract String detectAnomalies(
@Nullable ViewCaptureAnalyzer.AnalysisNode oldInfo,
@Nullable ViewCaptureAnalyzer.AnalysisNode newInfo, int frameN,
- long frameTimeNs);
+ long frameTimeNs, int windowSizePx);
}
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
index 6d9198f..8b88ace 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/FlashDetector.java
@@ -110,7 +110,7 @@
@Override
String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN,
- long frameTimeNs) {
+ long frameTimeNs, int windowSizePx) {
// Should we check when a view was visible for a short period, then its alpha became 0?
// Then 'lastVisible' time should be the last one still visible?
// Check only transitions of alpha between 0 and 1?
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java
new file mode 100644
index 0000000..a1ddcb0
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/PositionJumpDetector.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util.viewcapture_analysis;
+
+import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnalysisNode;
+
+import java.util.List;
+
+/**
+ * Anomaly detector that triggers an error when a view position jumps.
+ */
+final class PositionJumpDetector extends AnomalyDetector {
+ // Maximum allowed jump in "milliwindows", i.e. a 1/1000's of the maximum of the window
+ // dimensions.
+ private static final float JUMP_MIW = 250;
+
+ private static final String[] BORDER_NAMES = {"left", "top", "right", "bottom"};
+
+ // Commonly used parts of the paths to ignore.
+ private static final String CONTENT = "DecorView|LinearLayout|FrameLayout:id/content|";
+ private static final String DRAG_LAYER =
+ CONTENT + "LauncherRootView:id/launcher|DragLayer:id/drag_layer|";
+ private static final String RECENTS_DRAG_LAYER =
+ CONTENT + "LauncherRootView:id/launcher|RecentsDragLayer:id/drag_layer|";
+
+ private static final IgnoreNode IGNORED_NODES_ROOT = buildIgnoreNodesTree(List.of(
+ DRAG_LAYER + "SearchContainerView:id/apps_view",
+ DRAG_LAYER + "AppWidgetResizeFrame",
+ DRAG_LAYER + "LauncherAllAppsContainerView:id/apps_view",
+ CONTENT
+ + "AddItemDragLayer:id/add_item_drag_layer|AddItemWidgetsBottomSheet:id"
+ + "/add_item_bottom_sheet|LinearLayout:id/add_item_bottom_sheet_content",
+ DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container",
+ DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container",
+ DRAG_LAYER + "LauncherDragView",
+ RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel|TaskView",
+ CONTENT + "LauncherRootView:id/launcher|FloatingIconView",
+ DRAG_LAYER + "FloatingTaskView",
+ DRAG_LAYER + "LauncherRecentsView:id/overview_panel"
+ ));
+
+ // Per-AnalysisNode data that's specific to this detector.
+ private static class NodeData {
+ public boolean ignoreJumps;
+
+ // If ignoreNode is null, then this AnalysisNode node will be ignored if its parent is
+ // ignored.
+ // Otherwise, this AnalysisNode will be ignored if ignoreNode is a leaf i.e. has no
+ // children.
+ public IgnoreNode ignoreNode;
+ }
+
+ private NodeData getNodeData(AnalysisNode info) {
+ return (NodeData) info.detectorsData[detectorOrdinal];
+ }
+
+ @Override
+ void initializeNode(AnalysisNode info) {
+ final NodeData nodeData = new NodeData();
+ info.detectorsData[detectorOrdinal] = nodeData;
+
+ // If the parent view ignores jumps, its descendants will too.
+ final boolean parentIgnoresJumps = info.parent != null && getNodeData(
+ info.parent).ignoreJumps;
+ if (parentIgnoresJumps) {
+ nodeData.ignoreJumps = true;
+ return;
+ }
+
+ // Parent view doesn't ignore jumps.
+ // Initialize this AnalysisNode's ignore-node with the corresponding child of the
+ // ignore-node of the parent, if present.
+ final IgnoreNode parentIgnoreNode = info.parent != null
+ ? getNodeData(info.parent).ignoreNode
+ : IGNORED_NODES_ROOT;
+ nodeData.ignoreNode = parentIgnoreNode != null
+ ? parentIgnoreNode.children.get(info.nodeIdentity) : null;
+ // AnalysisNode will be ignored if the corresponding ignore-node is a leaf.
+ nodeData.ignoreJumps =
+ nodeData.ignoreNode != null && nodeData.ignoreNode.children.isEmpty();
+ }
+
+ @Override
+ String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN,
+ long frameTimeNs, int windowSizePx) {
+ // If the view is not present in the current frame, there can't be a jump detected in the
+ // current frame.
+ if (newInfo == null) return null;
+
+ // We only detect position jumps if the view was visible in the previous frame.
+ if (oldInfo == null || frameN != oldInfo.frameN + 1) return null;
+
+ final NodeData newNodeData = getNodeData(newInfo);
+ if (newNodeData.ignoreJumps) return null;
+
+ final float[] positionDiffs = {
+ newInfo.left - oldInfo.left,
+ newInfo.top - oldInfo.top,
+ newInfo.right - oldInfo.right,
+ newInfo.bottom - oldInfo.bottom
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ final float positionDiffAbs = Math.abs(positionDiffs[i]);
+ if (positionDiffAbs * 1000 > JUMP_MIW * windowSizePx) {
+ newNodeData.ignoreJumps = true;
+ return String.format("Position jump: %s jumped by %s",
+ BORDER_NAMES[i], positionDiffAbs);
+ }
+ }
+ return null;
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
index ccb4a1e..9459cc2 100644
--- a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
+++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java
@@ -36,7 +36,8 @@
// All detectors. They will be invoked in the order listed here.
private static final AnomalyDetector[] ANOMALY_DETECTORS = {
new AlphaJumpDetector(),
- new FlashDetector()
+ new FlashDetector(),
+ new PositionJumpDetector()
};
static {
@@ -52,6 +53,8 @@
// Window coordinates of the view.
public float left;
public float top;
+ public float right;
+ public float bottom;
// Visible scale and alpha, build recursively from the ancestor list.
public float scaleX;
@@ -81,7 +84,8 @@
@Override
public String toString() {
- return String.format("view window coordinates: (%s, %s)", left, top);
+ return String.format("view window coordinates: (%s, %s, %s, %s)",
+ left, top, right, bottom);
}
}
@@ -112,15 +116,33 @@
// As we go though frames, if a view becomes invisible, it stays in the map.
final Map<Integer, AnalysisNode> lastSeenNodes = new HashMap<>();
+ int windowWidthPx = -1;
+ int windowHeightPx = -1;
+
for (int frameN = 0; frameN < windowData.getFrameDataCount(); ++frameN) {
- analyzeFrame(frameN, windowData.getFrameData(frameN), viewCaptureData, lastSeenNodes,
- scrimClassIndex, anomalies);
+ final FrameData frame = windowData.getFrameData(frameN);
+ final ViewNode rootNode = frame.getNode();
+
+ // If the rotation or window size has changed, reset the analyzer state.
+ final boolean isFirstFrame = windowWidthPx != rootNode.getWidth()
+ || windowHeightPx != rootNode.getHeight();
+ if (isFirstFrame) {
+ windowWidthPx = rootNode.getWidth();
+ windowHeightPx = rootNode.getHeight();
+ lastSeenNodes.clear();
+ }
+
+ final int windowSizePx = Math.max(rootNode.getWidth(), rootNode.getHeight());
+
+ analyzeFrame(frameN, isFirstFrame, frame, viewCaptureData, lastSeenNodes,
+ scrimClassIndex, anomalies, windowSizePx);
}
}
- private static void analyzeFrame(int frameN, FrameData frame, ExportedData viewCaptureData,
+ private static void analyzeFrame(int frameN, boolean isFirstFrame, FrameData frame,
+ ExportedData viewCaptureData,
Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex,
- Map<String, String> anomalies) {
+ Map<String, String> anomalies, int windowSizePx) {
// Analyze the node tree starting from the root.
long frameTimeNs = frame.getTimestamp();
analyzeView(
@@ -128,12 +150,14 @@
frame.getNode(),
/* parent = */ null,
frameN,
+ isFirstFrame,
/* leftShift = */ 0,
/* topShift = */ 0,
viewCaptureData,
lastSeenNodes,
scrimClassIndex,
- anomalies);
+ anomalies,
+ windowSizePx);
// Analyze transitions when a view visible in the previous frame became invisible in the
// current one.
@@ -148,7 +172,8 @@
/* oldInfo = */ info,
/* newInfo = */ null,
anomalies,
- frameTimeNs)
+ frameTimeNs,
+ windowSizePx)
);
}
info.timeBecameInvisibleNs = info.alpha == 1 ? frameTimeNs : -1;
@@ -159,9 +184,9 @@
private static void analyzeView(long frameTimeNs, ViewNode viewCaptureNode, AnalysisNode parent,
int frameN,
- float leftShift, float topShift, ExportedData viewCaptureData,
+ boolean isFirstFrame, float leftShift, float topShift, ExportedData viewCaptureData,
Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex,
- Map<String, String> anomalies) {
+ Map<String, String> anomalies, int windowSizePx) {
// Skip analysis of invisible views
final float parentAlpha = parent != null ? parent.alpha : 1;
final float alpha = getVisibleAlpha(viewCaptureNode, parentAlpha);
@@ -182,6 +207,8 @@
final float top = topShift
+ (viewCaptureNode.getTop() + viewCaptureNode.getTranslationY()) * parentScaleY
+ viewCaptureNode.getHeight() * (parentScaleY - scaleY) / 2;
+ final float width = viewCaptureNode.getWidth() * scaleX;
+ final float height = viewCaptureNode.getHeight() * scaleY;
// Initialize new analysis node
final AnalysisNode newAnalysisNode = new AnalysisNode();
@@ -192,6 +219,8 @@
newAnalysisNode.parent = parent;
newAnalysisNode.left = left;
newAnalysisNode.top = top;
+ newAnalysisNode.right = left + width;
+ newAnalysisNode.bottom = top + height;
newAnalysisNode.scaleX = scaleX;
newAnalysisNode.scaleY = scaleY;
newAnalysisNode.alpha = alpha;
@@ -216,11 +245,11 @@
}
// Detect anomalies for the view.
- if (frameN != 0 && !viewCaptureNode.getWillNotDraw()) {
+ if (!isFirstFrame && !viewCaptureNode.getWillNotDraw()) {
Arrays.stream(ANOMALY_DETECTORS).forEach(
detector ->
detectAnomaly(detector, frameN, oldAnalysisNode, newAnalysisNode,
- anomalies, frameTimeNs)
+ anomalies, frameTimeNs, windowSizePx)
);
}
lastSeenNodes.put(hashcode, newAnalysisNode);
@@ -235,17 +264,19 @@
// transparent.
if (child.getClassnameIndex() == scrimClassIndex) break;
- analyzeView(frameTimeNs, child, newAnalysisNode, frameN, leftShiftForChildren,
+ analyzeView(frameTimeNs, child, newAnalysisNode, frameN, isFirstFrame,
+ leftShiftForChildren,
topShiftForChildren,
- viewCaptureData, lastSeenNodes, scrimClassIndex, anomalies);
+ viewCaptureData, lastSeenNodes, scrimClassIndex, anomalies, windowSizePx);
}
}
private static void detectAnomaly(AnomalyDetector detector, int frameN,
AnalysisNode oldAnalysisNode, AnalysisNode newAnalysisNode,
- Map<String, String> anomalies, long frameTimeNs) {
+ Map<String, String> anomalies, long frameTimeNs, int windowSizePx) {
final String maybeAnomaly =
- detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN, frameTimeNs);
+ detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN, frameTimeNs,
+ windowSizePx);
if (maybeAnomaly != null) {
AnalysisNode latestInfo = newAnalysisNode != null ? newAnalysisNode : oldAnalysisNode;
final String viewDiagPath = diagPathFromRoot(latestInfo);
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 7dd5827..ebcca00 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -18,8 +18,6 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-import static com.android.launcher3.tapl.LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS;
-import static com.android.launcher3.tapl.LauncherInstrumentation.EVENT_TOUCH_UP_TIS;
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
@@ -133,16 +131,6 @@
}
}
} else {
- if (mLauncher.isTablet()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_DOWN);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_UP);
- }
- if (mLauncher.isTrackpadGestureEnabled()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(
() -> mLauncher.waitForNavigationUiObject("recent_apps").click(),
@@ -203,7 +191,7 @@
final LauncherInstrumentation.GestureScope gestureScope =
zeroButtonToOverviewGestureStartsInLauncher()
- ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
+ ? LauncherInstrumentation.GestureScope.INSIDE
: LauncherInstrumentation.GestureScope.OUTSIDE_WITHOUT_PILFER;
mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(),
@@ -273,30 +261,10 @@
} else {
// Double press the recents button.
UiObject2 recentsButton = mLauncher.waitForNavigationUiObject("recent_apps");
- if (mLauncher.isTablet()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_DOWN);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_UP);
- }
- if (mLauncher.isTrackpadGestureEnabled()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL,
"clicking Recents button for the first time");
mLauncher.getOverview();
- if (mLauncher.isTablet()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_DOWN);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
- LauncherInstrumentation.EVENT_TOUCH_UP);
- }
- if (mLauncher.isTrackpadGestureEnabled()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.executeAndWaitForEvent(
() -> recentsButton.click(),
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 9a7710a..230be06 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -194,7 +194,7 @@
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
endPoint,
- LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER);
+ LauncherInstrumentation.GestureScope.INSIDE);
LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: "
+ "after drop");
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 262d5ff..e6fc244 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -99,17 +99,9 @@
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
private static final int GESTURE_STEP_MS = 16;
- static final Pattern EVENT_TOUCH_DOWN = getTouchEventPatternWithPointerCount("ACTION_DOWN");
- static final Pattern EVENT_TOUCH_UP = getTouchEventPatternWithPointerCount("ACTION_UP");
- private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPatternWithPointerCount(
- "ACTION_CANCEL");
static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
static final Pattern EVENT_START = Pattern.compile("start:");
- static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
- static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
- static final Pattern EVENT_TOUCH_CANCEL_TIS = getTouchEventPattern(
- "TouchInteractionService.onInputEvent", "ACTION_CANCEL");
static final Pattern EVENT_HOVER_ENTER_TIS = getTouchEventPatternTIS("ACTION_HOVER_ENTER");
static final Pattern EVENT_HOVER_EXIT_TIS = getTouchEventPatternTIS("ACTION_HOVER_EXIT");
static final Pattern EVENT_BUTTON_PRESS_TIS = getTouchEventPatternTIS("ACTION_BUTTON_PRESS");
@@ -139,7 +131,6 @@
// whether the gesture recognition triggers pilfer.
public enum GestureScope {
OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE,
- INSIDE_TO_OUTSIDE_WITHOUT_PILFER,
INSIDE_TO_OUTSIDE_WITH_KEYCODE, // For gestures that will trigger a keycode from TIS.
OUTSIDE_WITH_KEYCODE,
}
@@ -213,12 +204,6 @@
private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE;
private int mPointerCount = 0;
- private static Pattern getTouchEventPattern(String prefix, String action) {
- return Pattern.compile(
- prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
- + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?");
- }
-
private static Pattern getTouchEventPatternWithPointerCount(String prefix, String action,
int pointerCount) {
return Pattern.compile(
@@ -227,10 +212,6 @@
+ pointerCount);
}
- private static Pattern getTouchEventPatternWithPointerCount(String action) {
- return getTouchEventPatternWithPointerCount("Touch event", action, 1);
- }
-
private static Pattern getTouchEventPatternWithPointerCount(String action, int pointerCount) {
return getTouchEventPatternWithPointerCount("Touch event", action, pointerCount);
}
@@ -1072,14 +1053,6 @@
log("Hierarchy before clicking home:");
dumpViewHierarchy();
action = "clicking home button";
- if (isTablet()) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
- }
- if (isTrackpadGestureEnabled()) {
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
runToState(
waitForNavigationUiObject("home")::click,
@@ -1120,14 +1093,6 @@
10, false, gestureScope);
} else {
waitForNavigationUiObject("back").click();
- if (isTablet()) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
- }
- if (isTrackpadGestureEnabled()) {
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
}
if (launcherVisible) {
if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
@@ -1781,44 +1746,17 @@
boolean isTwoFingerTrackpadGesture = mTrackpadGestureType == TrackpadGestureType.TWO_FINGER;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
- if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER
- && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER
- && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE
- && (!isTrackpadGesture || isTwoFingerTrackpadGesture)) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
- }
- if (hasTIS && (isTrackpadGestureEnabled()
- || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- }
if (isTrackpadGesture) {
mPointerCount = 1;
pointerCount = mPointerCount;
}
break;
case MotionEvent.ACTION_UP:
- if (hasTIS && gestureScope != GestureScope.INSIDE
- && gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
+ if (hasTIS
&& (gestureScope == GestureScope.OUTSIDE_WITH_PILFER
|| gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) {
expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
}
- if (gestureScope != GestureScope.OUTSIDE_WITH_PILFER
- && gestureScope != GestureScope.OUTSIDE_WITHOUT_PILFER
- && gestureScope != GestureScope.OUTSIDE_WITH_KEYCODE
- && (!isTrackpadGesture || isTwoFingerTrackpadGesture)) {
- expectEvent(TestProtocol.SEQUENCE_MAIN,
- gestureScope == GestureScope.INSIDE
- || gestureScope == GestureScope.OUTSIDE_WITHOUT_PILFER
- ? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
- }
- if (hasTIS && (isTrackpadGestureEnabled()
- || getNavigationModel() != NavigationModel.THREE_BUTTON)) {
- expectEvent(TestProtocol.SEQUENCE_TIS,
- gestureScope == GestureScope.INSIDE_TO_OUTSIDE_WITH_KEYCODE
- || gestureScope == GestureScope.OUTSIDE_WITH_KEYCODE
- ? EVENT_TOUCH_CANCEL_TIS : EVENT_TOUCH_UP_TIS);
- }
break;
case MotionEvent.ACTION_HOVER_ENTER:
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_ENTER_TIS);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 2f7596e..bd2c9c1 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -19,8 +19,6 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.testing.shared.TestProtocol;
-
/**
* View containing overview actions
*/
@@ -51,13 +49,6 @@
"clicked screenshot button")) {
UiObject2 closeScreenshot = mLauncher.waitForSystemUiObject(
"screenshot_dismiss_image");
- if (mLauncher.isTrackpadGestureEnabled() || mLauncher.getNavigationModel()
- != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
- LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
- LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
- }
closeScreenshot.click();
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
"dismissed screenshot")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 8c3402f..d02e747 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -59,21 +59,23 @@
}
/** Find the web suggestion from search suggestion's title text */
- public void verifyWebSuggestIsPresent(String text) {
- ArrayList<UiObject2> goldenGateResults =
+ public SearchWebSuggestion findWebSuggestion(String text) {
+ ArrayList<UiObject2> webSuggestions =
new ArrayList<>(mLauncher.waitForObjectsInContainer(
mLauncher.waitForSystemLauncherObject(SEARCH_CONTAINER_RES_ID),
By.clazz(TextView.class)));
- boolean found = false;
- for(UiObject2 uiObject: goldenGateResults) {
+ for (UiObject2 uiObject: webSuggestions) {
String currentString = uiObject.getText();
if (currentString.equals(text)) {
- found = true;
+ return createWebSuggestion(uiObject);
}
}
- if (!found) {
- throw new IllegalStateException("Web suggestion title: " + text + " not found");
- }
+ mLauncher.fail("Web suggestion title: " + text + " not found");
+ return null;
+ }
+
+ protected SearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) {
+ return new SearchWebSuggestion(mLauncher, webSuggestion);
}
/** Find the total amount of views being displayed and return the size */
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
index c267c9e..6c6ab05 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromTaskbarQsb.java
@@ -35,4 +35,14 @@
protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
return new TaskbarAppIcon(mLauncher, icon);
}
+
+ @Override
+ public TaskbarSearchWebSuggestion findWebSuggestion(String text) {
+ return (TaskbarSearchWebSuggestion) super.findWebSuggestion(text);
+ }
+
+ @Override
+ protected TaskbarSearchWebSuggestion createWebSuggestion(UiObject2 webSuggestion) {
+ return new TaskbarSearchWebSuggestion(mLauncher, webSuggestion);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java b/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java
new file mode 100644
index 0000000..e4dec98
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SearchWebSuggestion.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.shared.TestProtocol;
+
+import java.util.regex.Pattern;
+
+/**
+ * Operations on a search web suggestion from a qsb.
+ */
+public class SearchWebSuggestion extends Launchable {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+
+ SearchWebSuggestion(LauncherInstrumentation launcher, UiObject2 object) {
+ super(launcher, object);
+ }
+
+ @Override
+ protected void expectActivityStartEvents() {
+ }
+
+ @Override
+ protected String launchableType() {
+ return "search web suggestion";
+ }
+
+ @Override
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("popup_container");
+ }
+
+ @Override
+ protected void addExpectedEventsForLongClick() {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, getLongClickEvent());
+ }
+
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java
new file mode 100644
index 0000000..cd8ce42
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarSearchWebSuggestion.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * Operations on a search web suggestion from the Taskbar qsb.
+ */
+public class TaskbarSearchWebSuggestion extends SearchWebSuggestion implements
+ SplitscreenDragSource {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+ TaskbarSearchWebSuggestion(LauncherInstrumentation launcher,
+ UiObject2 object) {
+ super(launcher, object);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+
+ /** This method requires public access, however should not be called in tests. */
+ @Override
+ public Launchable getLaunchable() {
+ return this;
+ }
+}