Merge "launcher: enable swipe down to open focused app" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 85e5ab0..b40a1d5 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -262,10 +262,6 @@
} else {
removeOutlineDrawings();
}
-
- if (mLauncher.getTaskbarUIController() != null) {
- mLauncher.getTaskbarUIController().onHotseatUpdated();
- }
}
private void removeOutlineDrawings() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 7340559..8b11154 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -48,7 +48,6 @@
private final BaseQuickstepLauncher mLauncher;
private final TaskbarStateHandler mTaskbarStateHandler;
- private final TaskbarHotseatController mHotseatController;
private final TaskbarActivityContext mContext;
private final TaskbarDragLayer mTaskbarDragLayer;
@@ -77,8 +76,6 @@
mLauncher = launcher;
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
- mHotseatController = new TaskbarHotseatController(
- mLauncher, mTaskbarView::updateHotseatItems);
}
@Override
@@ -91,7 +88,6 @@
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
- mHotseatController.init();
mLauncher.setTaskbarUIController(this);
mKeyguardController = taskbarControllers.taskbarKeyguardController;
@@ -106,7 +102,6 @@
mIconAlignmentForResumedState.finishAnimation();
mIconAlignmentForGestureState.finishAnimation();
- mHotseatController.cleanup();
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.setTaskbarUIController(null);
}
@@ -244,13 +239,6 @@
}
/**
- * Should be called when one or more items in the Hotseat have changed.
- */
- public void onHotseatUpdated() {
- mHotseatController.onHotseatUpdated();
- }
-
- /**
* @param ev MotionEvent in screen coordinates.
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 8279a47..be26913 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -85,5 +85,6 @@
rotationButtonController.onDestroy();
taskbarDragLayerController.onDestroy();
taskbarKeyguardController.onDestroy();
+ taskbarViewController.onDestroy();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
deleted file mode 100644
index 91cf7ef..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.taskbar;
-
-import android.view.View;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.ItemInfo;
-
-import java.util.function.Consumer;
-
-/**
- * Works with TaskbarController to update the TaskbarView's Hotseat items.
- */
-public class TaskbarHotseatController {
-
- private final BaseQuickstepLauncher mLauncher;
- private final Hotseat mHotseat;
- private final Consumer<ItemInfo[]> mTaskbarCallbacks;
- private final int mNumHotseatIcons;
-
- private final DragController.DragListener mDragListener = new DragController.DragListener() {
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
-
- @Override
- public void onDragEnd() {
- onHotseatUpdated();
- }
- };
-
- public TaskbarHotseatController(
- BaseQuickstepLauncher launcher, Consumer<ItemInfo[]> taskbarCallbacks) {
- mLauncher = launcher;
- mHotseat = mLauncher.getHotseat();
- mTaskbarCallbacks = taskbarCallbacks;
- mNumHotseatIcons = mLauncher.getDeviceProfile().numShownHotseatIcons;
- }
-
- protected void init() {
- mLauncher.getDragController().addDragListener(mDragListener);
- onHotseatUpdated();
- }
-
- protected void cleanup() {
- mLauncher.getDragController().removeDragListener(mDragListener);
- }
-
- /**
- * Called when any Hotseat item changes, and reports the new list of items to TaskbarController.
- */
- protected void onHotseatUpdated() {
- ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets();
- ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons];
- for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) {
- View child = shortcutsAndWidgets.getChildAt(i);
- Object tag = shortcutsAndWidgets.getChildAt(i).getTag();
- if (tag instanceof ItemInfo) {
- ItemInfo itemInfo = (ItemInfo) tag;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- // Since the hotseat might be laid out vertically or horizontally, use whichever
- // index is higher.
- int index = Math.max(lp.cellX, lp.cellY);
- if (0 <= index && index < hotseatItemInfos.length) {
- hotseatItemInfos[index] = itemInfo;
- }
- }
- }
-
- mTaskbarCallbacks.accept(hotseatItemInfos);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
new file mode 100644
index 0000000..fc5abd0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import android.util.SparseArray;
+import android.view.View;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Launcher model Callbacks for rendering taskbar.
+ */
+public class TaskbarModelCallbacks implements
+ BgDataModel.Callbacks, LauncherBindableItemsContainer {
+
+ private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
+ private List<ItemInfo> mPredictedItems = Collections.emptyList();
+
+ private final TaskbarActivityContext mContext;
+ private final TaskbarView mContainer;
+
+ private boolean mBindInProgress = false;
+
+ public TaskbarModelCallbacks(
+ TaskbarActivityContext context, TaskbarView container) {
+ mContext = context;
+ mContainer = container;
+ }
+
+ @Override
+ public void startBinding() {
+ mBindInProgress = true;
+ mHotseatItems.clear();
+ mPredictedItems = Collections.emptyList();
+ }
+
+ @Override
+ public void finishBindingItems(IntSet pagesBoundFirst) {
+ mBindInProgress = false;
+ commitItemsToUI();
+ }
+
+ @Override
+ public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
+ ArrayList<ItemInfo> addAnimated) {
+ boolean add1 = handleItemsAdded(addNotAnimated);
+ boolean add2 = handleItemsAdded(addAnimated);
+ if (add1 || add2) {
+ commitItemsToUI();
+ }
+ }
+
+ @Override
+ public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) {
+ if (handleItemsAdded(shortcuts)) {
+ commitItemsToUI();
+ }
+ }
+
+ private boolean handleItemsAdded(List<ItemInfo> items) {
+ boolean modified = false;
+ for (ItemInfo item : items) {
+ if (item.container == Favorites.CONTAINER_HOTSEAT) {
+ mHotseatItems.put(item.screenId, item);
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
+
+ @Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+ updateWorkspaceItems(updated, mContext);
+ }
+
+ @Override
+ public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
+ updateRestoreItems(updates, mContext);
+ }
+
+ @Override
+ public void mapOverItems(ItemOperator op) {
+ final int itemCount = mContainer.getChildCount();
+ for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+ View item = mContainer.getChildAt(itemIdx);
+ if (op.evaluate((ItemInfo) item.getTag(), item)) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) {
+ if (handleItemsRemoved(matcher)) {
+ commitItemsToUI();
+ }
+ }
+
+ private boolean handleItemsRemoved(ItemInfoMatcher matcher) {
+ boolean modified = false;
+ for (int i = mHotseatItems.size() - 1; i >= 0; i--) {
+ if (matcher.matchesInfo(mHotseatItems.valueAt(i))) {
+ modified = true;
+ mHotseatItems.removeAt(i);
+ }
+ }
+ return modified;
+ }
+
+ @Override
+ public void bindItemsModified(List<ItemInfo> items) {
+ boolean removed = handleItemsRemoved(ItemInfoMatcher.ofItems(items));
+ boolean added = handleItemsAdded(items);
+ if (removed || added) {
+ commitItemsToUI();
+ }
+ }
+
+ @Override
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ mPredictedItems = item.items;
+ commitItemsToUI();
+ }
+ }
+
+ private void commitItemsToUI() {
+ if (mBindInProgress) {
+ return;
+ }
+
+ ItemInfo[] hotseatItemInfos =
+ new ItemInfo[mContext.getDeviceProfile().numShownHotseatIcons];
+ int predictionSize = mPredictedItems.size();
+ int predictionNextIndex = 0;
+
+ for (int i = 0; i < hotseatItemInfos.length; i++) {
+ hotseatItemInfos[i] = mHotseatItems.get(i);
+ if (hotseatItemInfos[i] == null && predictionNextIndex < predictionSize) {
+ hotseatItemInfos[i] = mPredictedItems.get(predictionNextIndex);
+ hotseatItemInfos[i].screenId = i;
+ predictionNextIndex++;
+ }
+ }
+ mContainer.updateHotseatItems(hotseatItemInfos);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 50c26b3..94c0ebe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -24,6 +24,7 @@
import android.view.View;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
@@ -50,6 +51,8 @@
private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
this::updateTranslationY);
+ private final TaskbarModelCallbacks mModelCallbacks;
+
// Initialized in init.
private TaskbarControllers mControllers;
@@ -63,6 +66,7 @@
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4);
mTaskbarIconAlpha.setUpdateVisibility(true);
+ mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView);
}
public void init(TaskbarControllers controllers) {
@@ -71,6 +75,11 @@
mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
mTaskbarIconScaleForStash.updateValue(1f);
+ LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
+ }
+
+ public void onDestroy() {
+ LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
}
public boolean areIconsVisible() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index c115bbb..c46809a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -17,21 +17,17 @@
package com.android.launcher3.uioverrides;
import android.annotation.TargetApi;
-import android.content.Context;
import android.os.Build;
import android.provider.DeviceConfig;
import com.android.launcher3.config.FeatureFlags.DebugFlag;
-import java.util.ArrayList;
-
@TargetApi(Build.VERSION_CODES.P)
public class DeviceFlag extends DebugFlag {
public static final String NAMESPACE_LAUNCHER = "launcher";
private final boolean mDefaultValueInCode;
- ArrayList<Runnable> mListeners;
public DeviceFlag(String key, boolean defaultValue, String description) {
super(key, getDeviceValue(key, defaultValue), description);
@@ -44,53 +40,11 @@
}
@Override
- public void initialize(Context context) {
- super.initialize(context);
- if (mListeners == null) {
- mListeners = new ArrayList<>();
- registerDeviceConfigChangedListener(context);
- }
- }
-
- @Override
- public void addChangeListener(Context context, Runnable r) {
- if (mListeners == null) {
- initialize(context);
- }
- mListeners.add(r);
- }
-
- @Override
- public void removeChangeListener(Runnable r) {
- if (mListeners == null) {
- return;
- }
- mListeners.remove(r);
- }
-
- @Override
public boolean get() {
// Override this method in order to let Robolectric ShadowDeviceFlag to stub it.
return super.get();
}
- private void registerDeviceConfigChangedListener(Context context) {
- DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE_LAUNCHER,
- context.getMainExecutor(),
- properties -> {
- if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())
- || !properties.getKeyset().contains(key)) {
- return;
- }
- defaultValue = getDeviceValue(key, mDefaultValueInCode);
- initialize(context);
- for (Runnable r: mListeners) {
- r.run();
- }
- });
- }
-
protected static boolean getDeviceValue(String key, boolean defaultValue) {
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValue);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index ec9893c..2009cd7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,7 +17,6 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -55,7 +54,6 @@
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
@@ -84,7 +82,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@@ -231,15 +228,6 @@
}
@Override
- public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
- super.bindWorkspaceItemsChanged(updated);
- if (getTaskbarUIController() != null && updated.stream()
- .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
- getTaskbarUIController().onHotseatUpdated();
- }
- }
-
- @Override
public void onDestroy() {
super.onDestroy();
mHotseatPredictionController.destroy();
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 096ac6c..9180bd0 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -115,7 +115,6 @@
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -1026,9 +1025,6 @@
if (mRecentsView != null) {
int nearestPage = mRecentsView.getDestinationPage();
boolean isScrolling = false;
- // Update page scroll before snapping to page to make sure we snapped to the
- // position calculated with target gesture in mind.
- mRecentsView.updateScrollSynchronously();
if (mRecentsView.getNextPage() != nearestPage) {
// We shouldn't really scroll to the next page when swiping up to recents.
// Only allow settling on the next page if it's nearest to the center.
@@ -1110,7 +1106,8 @@
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
if (task.taskId == mGestureState.getRunningTaskId()
- && TaskInfoCompat.getActivityType(task) != ACTIVITY_TYPE_HOME) {
+ && task.configuration.windowConfiguration.getActivityType()
+ != ACTIVITY_TYPE_HOME) {
// Since this is an edge case, just cancel and relaunch with default activity
// options (since we don't know if there's an associated app icon to launch from)
endRunningWindowAnim(true /* cancel */);
@@ -1152,8 +1149,7 @@
boolean appCanEnterPip = !mDeviceState.isPipActive()
&& runningTaskTarget != null
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
- && TaskInfoCompat.isAutoEnterPipEnabled(
- runningTaskTarget.taskInfo.pictureInPictureParams);
+ && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
HomeAnimationFactory homeAnimFactory =
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
runningTaskTarget);
@@ -1187,7 +1183,8 @@
mLauncherTransitionController = null;
if (mRecentsView != null) {
- mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+ mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget(),
+ mTaskViewSimulator);
}
} else {
AnimatorSet animatorSet = new AnimatorSet();
@@ -1229,7 +1226,7 @@
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
- animatorSet, mGestureState.getEndTarget());
+ animatorSet, mGestureState.getEndTarget(), mTaskViewSimulator);
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
animatorSet.start();
@@ -1254,7 +1251,7 @@
final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
.startSwipePipToHome(taskInfo.topActivity,
- TaskInfoCompat.getTopActivityInfo(taskInfo),
+ taskInfo.topActivityInfo,
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
mDp.hotseatBarSizePx);
@@ -1263,9 +1260,9 @@
.setTaskId(runningTaskTarget.taskId)
.setComponentName(taskInfo.topActivity)
.setLeash(runningTaskTarget.leash.getSurfaceControl())
- .setSourceRectHint(TaskInfoCompat.getPipSourceRectHint(
- runningTaskTarget.taskInfo.pictureInPictureParams))
- .setAppBounds(TaskInfoCompat.getWindowConfigurationBounds(taskInfo))
+ .setSourceRectHint(
+ runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint())
+ .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds())
.setHomeToWindowPositionMap(homeToWindowPositionMap)
.setStartBounds(startRect)
.setDestinationBounds(destinationBounds)
@@ -1275,7 +1272,8 @@
// is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
if (homeRotation == ROTATION_0
&& (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
- builder.setFromRotation(mTaskViewSimulator, windowRotation);
+ builder.setFromRotation(mTaskViewSimulator, windowRotation,
+ taskInfo.displayCutoutInsets);
}
final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
AnimatorPlaybackController activityAnimationToHome =
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d963188..3bf79f1 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -36,6 +36,7 @@
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.SplitPlaceholderView;
@@ -89,8 +90,9 @@
*/
@Override
public void onPrepareGestureEndAnimation(
- @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
- super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+ @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+ TaskViewSimulator taskViewSimulator) {
+ super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulator);
if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
diff --git a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
index 552db1f..336f7d1 100644
--- a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
+++ b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
@@ -23,7 +23,6 @@
import android.os.Build;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskInfoCompat;
/**
* Utility class for interacting with the Assistant.
@@ -39,7 +38,7 @@
/** Returns true if the given task holds an Assistant activity that is excluded from recents. */
public static boolean isExcludedAssistant(TaskInfo info) {
return info != null
- && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
+ && info.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_ASSISTANT
&& (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 7488649..c0f5c14 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -301,6 +301,7 @@
private ComponentName mComponentName;
private SurfaceControl mLeash;
private Rect mSourceRectHint;
+ private Rect mDisplayCutoutInsets;
private Rect mAppBounds;
private Matrix mHomeToWindowPositionMap;
private RectF mStartBounds;
@@ -366,7 +367,8 @@
}
public Builder setFromRotation(TaskViewSimulator taskViewSimulator,
- @RecentsOrientedState.SurfaceRotation int fromRotation) {
+ @RecentsOrientedState.SurfaceRotation int fromRotation,
+ Rect displayCutoutInsets) {
if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
return this;
@@ -381,6 +383,9 @@
transformed.round(mDestinationBoundsTransformed);
mFromRotation = fromRotation;
+ if (displayCutoutInsets != null) {
+ mDisplayCutoutInsets = new Rect(displayCutoutInsets);
+ }
return this;
}
@@ -388,6 +393,14 @@
if (mDestinationBoundsTransformed.isEmpty()) {
mDestinationBoundsTransformed.set(mDestinationBounds);
}
+ // adjust the mSourceRectHint / mAppBounds by display cutout if applicable.
+ if (mSourceRectHint != null && mDisplayCutoutInsets != null) {
+ if (mFromRotation == Surface.ROTATION_90) {
+ mSourceRectHint.offset(mDisplayCutoutInsets.left, mDisplayCutoutInsets.top);
+ } else if (mFromRotation == Surface.ROTATION_270) {
+ mAppBounds.inset(mDisplayCutoutInsets);
+ }
+ }
return new SwipePipToHomeAnimator(mContext, mTaskId, mComponentName, mLeash,
mSourceRectHint, mAppBounds,
mHomeToWindowPositionMap, mStartBounds, mDestinationBounds,
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index 65dba33..1548268 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -72,14 +72,20 @@
mForegroundProperties.init(
mOriginalForeground.getConstantState().newDrawable().mutate());
setForeground(mForegroundProperties.mDrawable);
- mSourceView.setForeground(null);
+ Drawable clipPlaceholder =
+ mOriginalForeground.getConstantState().newDrawable().mutate();
+ clipPlaceholder.setAlpha(0);
+ mSourceView.setForeground(clipPlaceholder);
}
if (isSupportedDrawable(backgroundView.getBackground())) {
mOriginalBackground = backgroundView.getBackground();
mBackgroundProperties.init(
mOriginalBackground.getConstantState().newDrawable().mutate());
setBackground(mBackgroundProperties.mDrawable);
- mSourceView.setBackground(null);
+ Drawable clipPlaceholder =
+ mOriginalBackground.getConstantState().newDrawable().mutate();
+ clipPlaceholder.setAlpha(0);
+ mSourceView.setBackground(clipPlaceholder);
} else if (mOriginalForeground == null) {
mFallbackDrawable.setColor(fallbackBackgroundColor);
setBackground(mFallbackDrawable);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 411d0d8..6844f9f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -912,8 +912,8 @@
TaskViewUtils.composeRecentsLaunchAnimator(anim, taskView, apps, wallpaper, nonApps,
true /* launcherClosing */, mActivity.getStateManager(), this,
getDepthController());
- anim.start();
}
+ anim.start();
}
private void updateTaskStartIndex(View affectingView) {
@@ -1779,24 +1779,33 @@
* Called when a gesture from an app has finished, and an end target has been determined.
*/
public void onPrepareGestureEndAnimation(
- @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+ @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
+ TaskViewSimulator taskViewSimulator) {
+ mCurrentGestureEndTarget = endTarget;
+ if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+ setEnableFreeScroll(true);
+ updateGridProperties();
+ }
+
if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+ TaskView runningTaskView = getRunningTaskView();
+ float runningTaskPrimaryGridTranslation = 0;
+ if (indexOfChild(runningTaskView) != getNextPage()) {
+ // Apply the gird translation to running task unless it's being snapped to.
+ runningTaskPrimaryGridTranslation = mOrientationHandler.getPrimaryValue(
+ runningTaskView.getGridTranslationX(),
+ runningTaskView.getGridTranslationY());
+ }
if (animatorSet == null) {
setGridProgress(1);
+ taskViewSimulator.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation;
} else {
animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+ animatorSet.play(taskViewSimulator.taskPrimaryTranslation.animateToValue(
+ runningTaskPrimaryGridTranslation));
}
}
- mCurrentGestureEndTarget = endTarget;
- if (endTarget == GestureState.GestureEndTarget.NEW_TASK
- || endTarget == GestureState.GestureEndTarget.LAST_TASK) {
- // When switching to tasks in quick switch, ensures the snapped page's scroll maintain
- // invariant between quick switch and overview, to ensure a smooth animation transition.
- updateGridProperties();
- } else if (endTarget == GestureState.GestureEndTarget.RECENTS) {
- setEnableFreeScroll(true);
- }
}
/**
@@ -2083,12 +2092,6 @@
snappedTaskGridTranslationX = gridTranslations[snappedPage - mTaskViewStartIndex];
}
- for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
- taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
- + snappedTaskNonGridScrollAdjustment);
- }
-
// Use the accumulated translation of the row containing the last task.
float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
? topAccumulatedTranslationX : bottomAccumulatedTranslationX;
@@ -2131,17 +2134,23 @@
// Make sure there are enough space between snapped page and ClearAllButton, for the case
// of swiping up after quick switch.
if (snappedTaskView != null) {
- int distanceFromClearAll = longRowWidth - snappedTaskRowWidth;
+ int distanceFromClearAll = longRowWidth - snappedTaskRowWidth + mPageSpacing;
// ClearAllButton should be off screen when snapped task is in its snapped position.
int minimumDistance =
mTaskWidth - snappedTaskView.getLayoutParams().width
+ (mLastComputedGridSize.width() - mTaskWidth) / 2;
if (distanceFromClearAll < minimumDistance) {
int distanceDifference = minimumDistance - distanceFromClearAll;
- clearAllTotalTranslationX += mIsRtl ? -distanceDifference : distanceDifference;
+ snappedTaskGridTranslationX += mIsRtl ? distanceDifference : -distanceDifference;
}
}
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
+ + snappedTaskNonGridScrollAdjustment);
+ }
+
mClearAllButton.setGridTranslationPrimary(
clearAllTotalTranslationX - snappedTaskGridTranslationX);
mClearAllButton.setGridScrollOffset(
@@ -2505,12 +2514,15 @@
resetTaskVisuals();
int pageToSnapTo = mCurrentPage;
- if (finalNextFocusedTaskView != null) {
- pageToSnapTo = indexOfChild(finalNextFocusedTaskView);
- }
- if (dismissedIndex < pageToSnapTo || pageToSnapTo == (taskCount - 1)) {
+ if ((dismissedIndex < pageToSnapTo && !showAsGrid)
+ || pageToSnapTo == taskCount - 1) {
pageToSnapTo -= 1;
}
+ if (showAsGrid) {
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(RecentsView.this);
+ int currentPageScroll = getScrollForPage(pageToSnapTo);
+ mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+ }
removeViewInLayout(dismissedTaskView);
mTopRowIdSet.remove(dismissedTaskId);
@@ -2523,12 +2535,11 @@
mFocusedTaskId = finalNextFocusedTaskView.getTaskId();
mTopRowIdSet.remove(mFocusedTaskId);
finalNextFocusedTaskView.animateIconScaleAndDimIntoView();
- setCurrentPage(pageToSnapTo);
}
updateTaskSize(true);
// Update scroll and snap to page.
updateScrollSynchronously();
- snapToPageImmediately(pageToSnapTo);
+ setCurrentPage(pageToSnapTo);
dispatchScrollChanged();
}
}
@@ -2870,6 +2881,7 @@
mLastComputedTaskStartPushOutDistance = null;
mLastComputedTaskEndPushOutDistance = null;
updatePageOffsets();
+ mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
@@ -3632,18 +3644,28 @@
}
boolean pageScrollChanged = false;
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- float scrollDiff = 0;
- if (child instanceof TaskView) {
- scrollDiff = ((TaskView) child).getScrollAdjustment(showAsFullscreen, showAsGrid);
- } else if (child instanceof ClearAllButton) {
- scrollDiff = ((ClearAllButton) child).getScrollAdjustment(showAsFullscreen,
- showAsGrid);
- }
- final int pageScroll = newPageScrolls[i] + (int) scrollDiff;
+ int clearAllIndex = indexOfChild(mClearAllButton);
+ int clearAllScroll = 0;
+ int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+ if (clearAllIndex != -1 && clearAllIndex < outPageScrolls.length) {
+ float scrollDiff = mClearAllButton.getScrollAdjustment(showAsFullscreen, showAsGrid);
+ clearAllScroll = newPageScrolls[clearAllIndex] + (int) scrollDiff;
+ if (outPageScrolls[clearAllIndex] != clearAllScroll) {
+ pageScrollChanged = true;
+ outPageScrolls[clearAllIndex] = clearAllScroll;
+ }
+ }
+
+ final int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
+ int pageScroll = newPageScrolls[i + mTaskViewStartIndex] + (int) scrollDiff;
+ if ((mIsRtl && pageScroll < clearAllScroll)
+ || (!mIsRtl && pageScroll > clearAllScroll)) {
+ pageScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+ }
if (outPageScrolls[i] != pageScroll) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b272def..2e154f6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -794,7 +794,7 @@
mIconView.setRotation(orientationHandler.getDegreesRotated());
snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
mSnapshotView.setLayoutParams(snapshotParams);
- getThumbnail().getTaskOverlay().updateOrientationState(orientationState);
+ mSnapshotView.getTaskOverlay().updateOrientationState(orientationState);
}
private void setIconAndDimTransitionProgress(float progress, boolean invert) {
@@ -958,6 +958,7 @@
private void setNonGridScale(float nonGridScale) {
mNonGridScale = nonGridScale;
+ updateCornerRadius();
applyScale();
}
@@ -1296,18 +1297,21 @@
progress = Utilities.boundToRange(progress, 0, 1);
mFullscreenProgress = progress;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
- getThumbnail().getTaskOverlay().setFullscreenProgress(progress);
+ mSnapshotView.getTaskOverlay().setFullscreenProgress(progress);
- TaskThumbnailView thumbnail = getThumbnail();
- updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
+ updateCornerRadius();
- thumbnail.setFullscreenParams(mCurrentFullscreenParams);
+ mSnapshotView.setFullscreenParams(mCurrentFullscreenParams);
mOutlineProvider.updateParams(
mCurrentFullscreenParams,
mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
invalidateOutline();
}
+ private void updateCornerRadius() {
+ updateCurrentFullscreenParams(mSnapshotView.getPreviewPositionHelper());
+ }
+
void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
if (getRecentsView() == null) {
return;
@@ -1315,7 +1319,7 @@
mCurrentFullscreenParams.setProgress(
mFullscreenProgress,
getRecentsView().getScaleX(),
- getScaleX(),
+ mNonGridScale,
getWidth(), mActivity.getDeviceProfile(),
previewPositionHelper);
}
diff --git a/res/layout/launcher_preview_two_panel_layout.xml b/res/layout/launcher_preview_two_panel_layout.xml
new file mode 100644
index 0000000..7b227e0
--- /dev/null
+++ b/res/layout/launcher_preview_two_panel_layout.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<view class="com.android.launcher3.graphics.LauncherPreviewRenderer$LauncherPreviewLayout"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.launcher3.CellLayout
+ android:id="@+id/workspace_left"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:theme="@style/HomeScreenElementTheme"
+ launcher:containerType="workspace"
+ launcher:layout_constraintStart_toStartOf="parent"
+ launcher:layout_constraintTop_toTopOf="parent"
+ launcher:layout_constraintEnd_toStartOf="@id/workspace"
+ launcher:layout_constraintBottom_toBottomOf="parent"
+ launcher:pageIndicator="@+id/page_indicator" />
+
+ <com.android.launcher3.CellLayout
+ android:id="@+id/workspace"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:theme="@style/HomeScreenElementTheme"
+ launcher:containerType="workspace"
+ launcher:layout_constraintStart_toEndOf="@id/workspace_left"
+ launcher:layout_constraintTop_toTopOf="parent"
+ launcher:layout_constraintEnd_toEndOf="parent"
+ launcher:layout_constraintBottom_toBottomOf="parent"
+ launcher:pageIndicator="@+id/page_indicator" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <include
+ android:id="@+id/hotseat"
+ layout="@layout/hotseat" />
+
+</view>
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index 8e49fae..d2051e0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -131,7 +131,7 @@
mIdp.numDatabaseHotseatIcons);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
- task.migrate();
+ task.migrate(mIdp);
// Check hotseat items
Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
@@ -211,7 +211,7 @@
mIdp.numDatabaseHotseatIcons);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
- task.migrate();
+ task.migrate(mIdp);
// Check hotseat items
Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
@@ -259,7 +259,7 @@
mIdp.numDatabaseHotseatIcons);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
- task.migrate();
+ task.migrate(mIdp);
// Check hotseat items
Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 00278e4..f4447b1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2147,7 +2147,7 @@
mShakeAnimators.clear();
}
- private void commitTempPlacement() {
+ private void commitTempPlacement(View dragView) {
mTmpOccupied.copyTo(mOccupied);
int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
@@ -2165,7 +2165,7 @@
ItemInfo info = (ItemInfo) child.getTag();
// We do a null check here because the item info can be null in the case of the
// AllApps button in the hotseat.
- if (info != null) {
+ if (info != null && child != dragView) {
final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
|| info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
|| info.spanY != lp.cellVSpan);
@@ -2327,7 +2327,7 @@
animateItemsToSolution(swapSolution, dragView, commit);
if (commit) {
- commitTempPlacement();
+ commitTempPlacement(null);
completeAndClearReorderPreviewAnimations();
setItemPlacementDirty(false);
} else {
@@ -2421,7 +2421,8 @@
if (!DESTRUCTIVE_REORDER &&
(mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL)) {
- commitTempPlacement();
+ // Since the temp solution didn't update dragView, don't commit it either
+ commitTempPlacement(dragView);
completeAndClearReorderPreviewAnimations();
setItemPlacementDirty(false);
} else {
@@ -2877,7 +2878,7 @@
directionVector, null, false, configuration).isSolution) {
if (commitConfig) {
copySolutionToTempState(configuration, null);
- commitTempPlacement();
+ commitTempPlacement(null);
// undo marking cells occupied since there is actually nothing being placed yet.
mOccupied.markCells(0, mCountY - 1, mCountX, 1, false);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 89b44a3..78a8a97 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -48,7 +48,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
-import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -596,7 +595,7 @@
}
onDeviceProfileInitiated();
- mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true);
+ mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this);
}
public RotationHelper getRotationHelper() {
@@ -2598,9 +2597,6 @@
mPendingActivityResult = null;
}
- ItemInstallQueue.INSTANCE.get(this)
- .resumeModelPush(FLAG_LOADER_RUNNING);
-
int currentPage = pagesBoundFirst != null && !pagesBoundFirst.isEmpty()
? pagesBoundFirst.getArray().get(0) : PagedView.INVALID_PAGE;
// When undoing the removal of the last item on a page, return to that page.
@@ -2677,7 +2673,7 @@
@Override
public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
if (!updated.isEmpty()) {
- mWorkspace.updateShortcuts(updated);
+ mWorkspace.updateWorkspaceItems(updated, this);
PopupContainerWithArrow.dismissInvalidPopup(this);
}
}
@@ -2689,7 +2685,7 @@
*/
@Override
public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
- mWorkspace.updateRestoreItems(updates);
+ mWorkspace.updateRestoreItems(updates, this);
}
/**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index eef3980..6966abf 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -144,9 +144,10 @@
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
+ public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges,
+ @Nullable Callbacks owner) {
return new ModelWriter(mApp.getContext(), this, mBgDataModel,
- hasVerticalHotseat, verifyChanges);
+ hasVerticalHotseat, verifyChanges, owner);
}
@Override
@@ -329,7 +330,7 @@
public boolean addCallbacksAndLoad(Callbacks callbacks) {
synchronized (mLock) {
addCallbacks(callbacks);
- return startLoader();
+ return startLoader(new Callbacks[] { callbacks });
}
}
@@ -349,26 +350,32 @@
* @return true if the page could be bound synchronously.
*/
public boolean startLoader() {
+ return startLoader(new Callbacks[0]);
+ }
+
+ private boolean startLoader(Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {
- // Don't bother to start the thread if we know it's not going to do anything
- final Callbacks[] callbacksList = getCallbacks();
+ // If there is already one running, tell it to stop.
+ boolean wasRunning = stopLoader();
+ boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
+ boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
+ final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
+
if (callbacksList.length > 0) {
// Clear any pending bind-runnables from the synchronized load process.
for (Callbacks cb : callbacksList) {
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
- // If there is already one running, tell it to stop.
- stopLoader();
LoaderResults loaderResults = new LoaderResults(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
- if (mModelLoaded && !mIsLoaderTaskRunning) {
+ if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
- loaderResults.bindWorkspace();
+ loaderResults.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
loaderResults.bindAllApps();
@@ -387,7 +394,7 @@
* If there is already a loader task running, tell it to stop.
* @return true if an existing loader was stopped.
*/
- public boolean stopLoader() {
+ private boolean stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
mLoaderTask = null;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 204913a..20f5f9b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -104,6 +104,10 @@
@ViewDebug.ExportedProperty(category = "launcher")
protected int mCurrentPage;
+ // Difference between current scroll position and mCurrentPage's page scroll. Used to maintain
+ // relative scroll position unchanged in updateCurrentPageScroll. Cleared when snapping to a
+ // page.
+ protected int mCurrentPageScrollDiff;
@ViewDebug.ExportedProperty(category = "launcher")
protected int mNextPage = INVALID_PAGE;
@@ -247,7 +251,7 @@
// If the current page is invalid, just reset the scroll position to zero
int newPosition = 0;
if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
- newPosition = getScrollForPage(mCurrentPage);
+ newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
}
mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
@@ -452,6 +456,7 @@
* to provide custom behavior during animation.
*/
protected void onPageEndTransition() {
+ mCurrentPageScrollDiff = 0;
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
@@ -1082,26 +1087,25 @@
protected float getScrollProgress(int screenCenter, View v, int page) {
final int halfScreenSize = getMeasuredWidth() / 2;
-
int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
- int count = getChildCount();
+ int panelCount = getPanelCount();
+ int pageCount = getChildCount();
- final int totalDistance;
-
- int adjacentPage = page + 1;
+ int adjacentPage = page + panelCount;
if ((delta < 0 && !mIsRtl) || (delta > 0 && mIsRtl)) {
- adjacentPage = page - 1;
+ adjacentPage = page - panelCount;
}
- if (adjacentPage < 0 || adjacentPage > count - 1) {
- totalDistance = v.getMeasuredWidth() + mPageSpacing;
+ final int totalDistance;
+ if (adjacentPage < 0 || adjacentPage > pageCount - 1) {
+ totalDistance = (v.getMeasuredWidth() + mPageSpacing) * panelCount;
} else {
totalDistance = Math.abs(getScrollForPage(adjacentPage) - getScrollForPage(page));
}
float scrollProgress = delta / (totalDistance * 1.0f);
scrollProgress = Math.min(scrollProgress, MAX_SCROLL_PROGRESS);
- scrollProgress = Math.max(scrollProgress, - MAX_SCROLL_PROGRESS);
+ scrollProgress = Math.max(scrollProgress, -MAX_SCROLL_PROGRESS);
return scrollProgress;
}
@@ -1644,7 +1648,7 @@
public boolean scrollLeft() {
if (getNextPage() > 0) {
- snapToPage(getNextPage() - 1);
+ snapToPage(getNextPage() - getPanelCount());
return true;
}
return mAllowOverScroll;
@@ -1652,7 +1656,7 @@
public boolean scrollRight() {
if (getNextPage() < getChildCount() - 1) {
- snapToPage(getNextPage() + 1);
+ snapToPage(getNextPage() + getPanelCount());
return true;
}
return mAllowOverScroll;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5689394..a585be6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -83,7 +83,6 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.logger.LauncherAtom;
@@ -105,6 +104,7 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.OverlayEdgeEffect;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.RunnableList;
@@ -123,7 +123,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -136,7 +135,7 @@
public class Workspace extends PagedView<WorkspacePageIndicator>
implements DropTarget, DragSource, View.OnTouchListener,
DragController.DragListener, Insettable, StateHandler<LauncherState>,
- WorkspaceLayoutManager {
+ WorkspaceLayoutManager, LauncherBindableItemsContainer {
/** The value that {@link #mTransitionProgress} must be greater than for
* {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -3009,9 +3008,9 @@
* @param user The user of the app to match.
*/
public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
- final Workspace.ItemOperator preferredItem = (ItemInfo info, View view) ->
+ final ItemOperator preferredItem = (ItemInfo info, View view) ->
info != null && info.id == preferredItemId;
- final Workspace.ItemOperator preferredItemInFolder = (info, view) -> {
+ final ItemOperator preferredItemInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
@@ -3022,14 +3021,14 @@
}
return false;
};
- final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
+ final ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
info != null
&& info.itemType == ITEM_TYPE_APPLICATION
&& info.user.equals(user)
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(),
packageName);
- final Workspace.ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
+ final ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
@@ -3148,22 +3147,7 @@
stripEmptyScreens();
}
- public interface ItemOperator {
- /**
- * Process the next itemInfo, possibly with side-effect on the next item.
- *
- * @param info info for the shortcut
- * @param view view for the shortcut
- * @return true if done, false to continue the map
- */
- boolean evaluate(ItemInfo info, View view);
- }
-
- /**
- * Map the operator over the shortcuts and widgets, return the first-non-null value.
- *
- * @param op the operator to map over the shortcuts
- */
+ @Override
public void mapOverItems(ItemOperator op) {
for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
if (mapOverCellLayout(layout, op) != null) {
@@ -3189,31 +3173,6 @@
return null;
}
- void updateShortcuts(List<WorkspaceItemInfo> shortcuts) {
- final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
- ItemOperator op = (info, v) -> {
- if (v instanceof BubbleTextView && updates.contains(info)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- BubbleTextView shortcut = (BubbleTextView) v;
- Drawable oldIcon = shortcut.getIcon();
- boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
- && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
- shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
- } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
- ((FolderIcon) v).updatePreviewItems(updates::contains);
- }
-
- // Iterate all items
- return false;
- };
-
- mapOverItems(op);
- Folder openFolder = Folder.getOpen(mLauncher);
- if (openFolder != null) {
- openFolder.iterateOverItems(op);
- }
- }
-
public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
final PackageUserKey packageUserKey = new PackageUserKey(null, null);
Predicate<ItemInfo> matcher = info -> !packageUserKey.updateFromItemInfo(info)
@@ -3253,28 +3212,6 @@
removeItemsByMatcher(matcher);
}
- public void updateRestoreItems(final HashSet<ItemInfo> updates) {
- ItemOperator op = (info, v) -> {
- if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
- && updates.contains(info)) {
- ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
- } else if (v instanceof PendingAppWidgetHostView
- && info instanceof LauncherAppWidgetInfo
- && updates.contains(info)) {
- ((PendingAppWidgetHostView) v).applyState();
- } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
- ((FolderIcon) v).updatePreviewItems(updates::contains);
- }
- // process all the shortcuts
- return false;
- };
- mapOverItems(op);
- Folder folder = Folder.getOpen(mLauncher);
- if (folder != null) {
- folder.iterateOverItems(op);
- }
- }
-
public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
if (!changedInfo.isEmpty()) {
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 516af59..d2c71b2 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -477,10 +477,6 @@
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
- if (mWorkModeSwitch != null) {
- ((ViewGroup) mWorkModeSwitch.getParent()).removeView(mWorkModeSwitch);
- mWorkModeSwitch = null;
- }
}
setupHeader();
@@ -532,7 +528,7 @@
@Override
public void onActivePageChanged(int currentActivePage) {
- mHeader.setMainActive(currentActivePage == 0);
+ mHeader.setMainActive(currentActivePage == AdapterHolder.MAIN);
if (mAH[currentActivePage].recyclerView != null) {
mAH[currentActivePage].recyclerView.bindFastScrollbar();
}
@@ -541,6 +537,14 @@
mWorkModeSwitch.setWorkTabVisible(currentActivePage == AdapterHolder.WORK
&& mAllAppsStore.hasModelFlag(
FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
+
+ if (currentActivePage == AdapterHolder.WORK) {
+ if (mWorkModeSwitch.getParent() == null) {
+ addView(mWorkModeSwitch);
+ }
+ } else {
+ removeView(mWorkModeSwitch);
+ }
}
}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 30c3417..92bc19a 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -87,6 +87,7 @@
if (accessibilityManager == null) return;
sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
+ Log.d(TestProtocol.HOME_TO_OVERVIEW_FLAKY, "sendPauseDetectedEventToTest");
}
private static void sendEventToTest(
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1fe50f8..1779ddb 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -295,7 +295,7 @@
public static class BooleanFlag {
public final String key;
- public boolean defaultValue;
+ public final boolean defaultValue;
public BooleanFlag(String key, boolean defaultValue) {
this.key = key;
@@ -314,16 +314,12 @@
protected StringBuilder appendProps(StringBuilder src) {
return src.append(key).append(", defaultValue=").append(defaultValue);
}
-
- public void addChangeListener(Context context, Runnable r) { }
-
- public void removeChangeListener(Runnable r) {}
}
public static class DebugFlag extends BooleanFlag {
public final String description;
- private boolean mCurrentValue;
+ protected boolean mCurrentValue;
public DebugFlag(String key, boolean defaultValue, String description) {
super(key, defaultValue);
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 55be4a4..6bd6261 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -189,10 +189,18 @@
if (appWidgetHostView != null) {
bounds = new Rect();
appWidgetHostView.getSourceVisualDragBounds(bounds);
- bounds.offset(appWidgetHostView.getLeft() - (int) mLastTouchPos.x,
- appWidgetHostView.getTop() - (int) mLastTouchPos.y);
- listener = new PinItemDragListener(mRequest, bounds,
- appWidgetHostView.getMeasuredWidth(), appWidgetHostView.getMeasuredWidth());
+ float appWidgetHostViewScale = mWidgetCell.getAppWidgetHostViewScale();
+ int xOffset =
+ appWidgetHostView.getLeft() - (int) (mLastTouchPos.x * appWidgetHostViewScale);
+ int yOffset = appWidgetHostView.getTop()
+ - (int) (mLastTouchPos.y * mWidgetCell.getAppWidgetHostViewScale());
+ bounds.offset(xOffset, yOffset);
+ listener = new PinItemDragListener(
+ mRequest,
+ bounds,
+ appWidgetHostView.getMeasuredWidth(),
+ appWidgetHostView.getMeasuredWidth(),
+ appWidgetHostView.getScaleX());
} else {
bounds = img.getBitmapBounds();
bounds.offset(img.getLeft() - (int) mLastTouchPos.x,
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 1f93730..f2ab96c 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -148,6 +148,12 @@
addView(content, new LayoutParams(width, height));
+ // If there is already a scale set on the content, we don't want to clip the children.
+ if (content.getScaleX() != 1 || content.getScaleY() != 1) {
+ setClipChildren(false);
+ setClipToPadding(false);
+ }
+
final float scale = (width + finalScaleDps) / width;
// Set the initial scale to avoid any jumps
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index 2bdf8a0..af43ae8 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -48,12 +48,19 @@
private final PinItemRequest mRequest;
private final CancellationSignal mCancelSignal;
+ private final float mPreviewScale;
public PinItemDragListener(PinItemRequest request, Rect previewRect,
int previewBitmapWidth, int previewViewWidth) {
+ this(request, previewRect, previewBitmapWidth, previewViewWidth, /* previewScale= */ 1f);
+ }
+
+ public PinItemDragListener(PinItemRequest request, Rect previewRect,
+ int previewBitmapWidth, int previewViewWidth, float previewScale) {
super(previewRect, previewBitmapWidth, previewViewWidth);
mRequest = request;
mCancelSignal = new CancellationSignal();
+ mPreviewScale = previewScale;
}
@Override
@@ -98,7 +105,7 @@
PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
- dragHelper.setRemoteViewsPreview(getPreview(mRequest));
+ dragHelper.setRemoteViewsPreview(getPreview(mRequest), mPreviewScale);
}
return dragHelper;
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 22bb56c..7187188 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -75,7 +75,6 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
@@ -94,6 +93,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -1196,8 +1196,7 @@
}
void replaceFolderWithFinalItem() {
- mLauncherDelegate.replaceFolderWithFinalItem(this);
- mDestroyed = true;
+ mDestroyed = mLauncherDelegate.replaceFolderWithFinalItem(this);
}
public boolean isDestroyed() {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d076be6..f005b61 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -679,6 +679,7 @@
@Override
public void onAdd(WorkspaceItemInfo item, int rank) {
+ updatePreviewItems(false);
boolean wasDotted = mDotInfo.hasDot();
mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
boolean isDotted = mDotInfo.hasDot();
@@ -690,6 +691,7 @@
@Override
public void onRemove(List<WorkspaceItemInfo> items) {
+ updatePreviewItems(false);
boolean wasDotted = mDotInfo.hasDot();
items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
boolean isDotted = mDotInfo.hasDot();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3d2884a..65991e4 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,12 +41,12 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index f7d8e8c..e599e8c 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -93,7 +93,7 @@
}
}
- void replaceFolderWithFinalItem(Folder folder) {
+ boolean replaceFolderWithFinalItem(Folder folder) {
// Add the last remaining child to the workspace in place of the folder
Runnable onCompleteRunnable = new Runnable() {
@Override
@@ -147,6 +147,7 @@
} else {
onCompleteRunnable.run();
}
+ return true;
}
@@ -191,7 +192,7 @@
ModelWriter getModelWriter() {
if (mWriter == null) {
mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
- .getWriter(false, false);
+ .getWriter(false, false, null);
}
return mWriter;
}
@@ -205,7 +206,9 @@
}
@Override
- void replaceFolderWithFinalItem(Folder folder) { }
+ boolean replaceFolderWithFinalItem(Folder folder) {
+ return false;
+ }
@Override
boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 1127ff9..1da8028 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -208,7 +208,7 @@
private final LayoutInflater mHomeElementInflater;
private final InsettableFrameLayout mRootView;
private final Hotseat mHotseat;
- private final CellLayout mWorkspace;
+ private final Map<Integer, CellLayout> mWorkspaceScreens = new HashMap<>();
private final AppWidgetHost mAppWidgetHost;
private final SparseIntArray mWallpaperColorResources;
@@ -254,19 +254,31 @@
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
mHomeElementInflater.setFactory2(this);
+ int layoutRes = mDp.isTwoPanels ? R.layout.launcher_preview_two_panel_layout
+ : R.layout.launcher_preview_layout;
mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
- R.layout.launcher_preview_layout, null, false);
+ layoutRes, null, false);
mRootView.setInsets(mInsets);
measureView(mRootView, mDp.widthPx, mDp.heightPx);
mHotseat = mRootView.findViewById(R.id.hotseat);
mHotseat.resetLayout(false);
- mWorkspace = mRootView.findViewById(R.id.workspace);
- mWorkspace.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
+ if (mDp.isTwoPanels) {
+ CellLayout leftPanel = mRootView.findViewById(R.id.workspace_left);
+ leftPanel.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
+ mDp.workspacePadding.top,
+ mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
+ mDp.workspacePadding.bottom);
+ mWorkspaceScreens.put(LEFT_PANEL_ID, leftPanel);
+ }
+
+ CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
+ firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.top,
mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.bottom);
+ mWorkspaceScreens.put(FIRST_SCREEN_ID, firstScreen);
if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
@@ -335,18 +347,20 @@
@Override
public CellLayout getScreenWithId(int screenId) {
- return mWorkspace;
+ return mWorkspaceScreens.get(screenId);
}
private void inflateAndAddIcon(WorkspaceItemInfo info) {
+ CellLayout screen = mWorkspaceScreens.get(info.screenId);
BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
- R.layout.app_icon, mWorkspace, false);
+ R.layout.app_icon, screen, false);
icon.applyFromWorkspaceItem(info);
addInScreenFromBind(icon, info);
}
private void inflateAndAddFolder(FolderInfo info) {
- FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
+ CellLayout screen = mWorkspaceScreens.get(info.screenId);
+ FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, screen,
info);
addInScreenFromBind(folderIcon, info);
}
@@ -396,7 +410,8 @@
}
private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
- View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
+ CellLayout screen = mWorkspaceScreens.get(info.screenId);
+ View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
if (view != null) {
addInScreenFromBind(view, info);
}
@@ -428,8 +443,7 @@
ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
- IntSet currentScreenIds = IntSet.wrap(0);
- // TODO(b/185508060): support two panel preview.
+ IntSet currentScreenIds = IntSet.wrap(mWorkspaceScreens.keySet());
filterCurrentWorkspaceItems(currentScreenIds, dataModel.workspaceItems,
currentWorkspaceItems, otherWorkspaceItems);
filterCurrentWorkspaceItems(currentScreenIds, dataModel.appWidgets, currentAppWidgets,
@@ -487,12 +501,13 @@
// Add first page QSB
if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
View qsb = mHomeElementInflater.inflate(
- R.layout.search_container_workspace, mWorkspace, false);
+ R.layout.search_container_workspace, firstScreen, false);
CellLayout.LayoutParams lp =
- new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
+ new CellLayout.LayoutParams(0, 0, firstScreen.getCountX(), 1);
lp.canReorder = false;
- mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
+ firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
measureView(mRootView, mDp.widthPx, mDp.heightPx);
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index df49359..13c83be 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -38,11 +38,13 @@
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.GridSizeMigrationTask;
@@ -163,11 +165,17 @@
@Override
public void run() {
+ DeviceProfile deviceProfile = mIdp.getDeviceProfile(mContext);
+ String query = (deviceProfile.isTwoPanels ? LauncherSettings.Favorites.SCREEN
+ + " = " + Workspace.LEFT_PANEL_ID + " or " : "")
+ + LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
+ + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
- LauncherSettings.Favorites.SCREEN + " = 0 or "
- + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+ query);
+
MAIN_EXECUTOR.execute(() -> {
+ mBgDataModel.isLeftPanelShown = deviceProfile.isTwoPanels;
renderView(previewContext, mBgDataModel, mWidgetProvidersMap);
mOnDestroyCallbacks.add(previewContext::onDestroy);
});
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 30755e3..c202d8d 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -73,7 +74,7 @@
/**
* Binds all loaded data to actual views on the main thread.
*/
- public void bindWorkspace() {
+ public void bindWorkspace(boolean incrementBindId) {
// Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
@@ -85,7 +86,9 @@
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.extraItems.forEach(extraItems::add);
- mBgDataModel.lastBindId++;
+ if (incrementBindId) {
+ mBgDataModel.lastBindId++;
+ }
mMyBindingId = mBgDataModel.lastBindId;
}
@@ -217,7 +220,11 @@
bindAppWidgets(otherAppWidgets, pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIndices), pendingExecutor);
pendingExecutor.execute(
- () -> MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT));
+ () -> {
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ .resumeModelPush(FLAG_LOADER_RUNNING);
+ });
executeCallbacksTask(
c -> {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index ad553d5..a3a4717 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -90,7 +90,7 @@
public ModelWriter getModelWriter() {
// Updates from model task, do not deal with icon position in hotseat. Also no need to
// verify changes as the ModelTasks always push the changes to callbacks
- return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
+ return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, null);
}
public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index ba825ca..dd4c3c3 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -477,6 +477,11 @@
ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated) { }
/**
+ * Called when some persistent property of an item is modified
+ */
+ default void bindItemsModified(List<ItemInfo> items) { }
+
+ /**
* Binds updated incremental download progress
*/
default void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 8a1d73e..3935bcf 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -41,6 +41,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
@@ -183,7 +184,7 @@
Point targetSize = new Point(idp.numColumns, idp.numRows);
GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
- task.migrate();
+ task.migrate(idp);
if (!migrateForPreview) {
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -210,7 +211,7 @@
}
@VisibleForTesting
- protected boolean migrate() {
+ protected boolean migrate(InvariantDeviceProfile idp) {
if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
return false;
}
@@ -224,7 +225,17 @@
Collections.sort(mWorkspaceDiff);
// Migrate workspace.
+ // First we create a collection of the screens
+ List<Integer> screens = new ArrayList<>();
+ if (idp.getDeviceProfile(mContext).isTwoPanels) {
+ screens.add(Workspace.LEFT_PANEL_ID);
+ }
for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
+ screens.add(screenId);
+ }
+
+ // Then we place the items on the screens
+ for (int screenId : screens) {
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
@@ -236,6 +247,8 @@
}
}
+ // In case the new grid is smaller, there might be some leftover items that don't fit on
+ // any of the screens, in this case we add them to new screens until all of them are placed.
int screenId = mDestReader.mLastScreenId + 1;
while (!mWorkspaceDiff.isEmpty()) {
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d5b5452..31ca6e7 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -210,7 +210,7 @@
}
verifyNotStopped();
- mResults.bindWorkspace();
+ mResults.bindWorkspace(true /* incrementBindId */);
logASplit(logger, "bindWorkspace");
mModelDelegate.workspaceLoadComplete();
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 2b93118..55edfd4 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -23,12 +23,13 @@
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -36,19 +37,22 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.Executor;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -63,7 +67,10 @@
private final Context mContext;
private final LauncherModel mModel;
private final BgDataModel mBgDataModel;
- private final Handler mUiHandler;
+ private final LooperExecutor mUiExecutor;
+
+ @Nullable
+ private final Callbacks mOwner;
private final boolean mHasVerticalHotseat;
private final boolean mVerifyChanges;
@@ -73,13 +80,15 @@
private boolean mPreparingToUndo;
public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
- boolean hasVerticalHotseat, boolean verifyChanges) {
+ boolean hasVerticalHotseat, boolean verifyChanges,
+ @Nullable Callbacks owner) {
mContext = context;
mModel = model;
mBgDataModel = dataModel;
mHasVerticalHotseat = hasVerticalHotseat;
mVerifyChanges = verifyChanges;
- mUiHandler = new Handler(Looper.getMainLooper());
+ mOwner = owner;
+ mUiExecutor = Executors.MAIN_EXECUTOR;
}
private void updateItemInfoProps(
@@ -162,6 +171,8 @@
public void moveItemInDatabase(final ItemInfo item,
int container, int screenId, int cellX, int cellY) {
updateItemInfoProps(item, container, screenId, cellX, cellY);
+ notifyItemModified(item);
+
enqueueDeleteRunnable(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
@@ -178,6 +189,7 @@
public void moveItemsInDatabase(final ArrayList<ItemInfo> items, int container, int screen) {
ArrayList<ContentValues> contentValues = new ArrayList<>();
int count = items.size();
+ notifyOtherCallbacks(c -> c.bindItemsModified(items));
for (int i = 0; i < count; i++) {
ItemInfo item = items.get(i);
@@ -203,8 +215,9 @@
updateItemInfoProps(item, container, screenId, cellX, cellY);
item.spanX = spanX;
item.spanY = spanY;
+ notifyItemModified(item);
- ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () ->
+ MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
@@ -219,13 +232,18 @@
* Update an item to the database in a specified container.
*/
public void updateItemInDatabase(ItemInfo item) {
- ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> {
+ notifyItemModified(item);
+ MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
return writer;
}));
}
+ private void notifyItemModified(ItemInfo item) {
+ notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item)));
+ }
+
/**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
@@ -236,10 +254,11 @@
final ContentResolver cr = mContext.getContentResolver();
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
+ notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false));
ModelVerifier verifier = new ModelVerifier();
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- ((Executor) MODEL_EXECUTOR).execute(() -> {
+ MODEL_EXECUTOR.execute(() -> {
// Write the item on background thread, as some properties might have been updated in
// the background.
final ContentWriter writer = new ContentWriter(mContext);
@@ -281,6 +300,7 @@
(item) -> item.getTargetComponent() == null ? ""
: item.getTargetComponent().getPackageName()).collect(
Collectors.joining(",")), new Exception());
+ notifyDelete(items);
enqueueDeleteRunnable(() -> {
for (ItemInfo item : items) {
final Uri uri = Favorites.getContentUri(item.id);
@@ -297,6 +317,7 @@
*/
public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
ModelVerifier verifier = new ModelVerifier();
+ notifyDelete(Collections.singleton(info));
enqueueDeleteRunnable(() -> {
ContentResolver cr = mContext.getContentResolver();
@@ -315,6 +336,7 @@
* Deletes the widget info and the widget id.
*/
public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
+ notifyDelete(Collections.singleton(info));
if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
@@ -323,6 +345,10 @@
deleteItemFromDatabase(info);
}
+ private void notifyDelete(Collection<? extends ItemInfo> items) {
+ notifyOtherCallbacks(c -> c.bindWorkspaceComponentsRemoved(ItemInfoMatcher.ofItems(items)));
+ }
+
/**
* Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
* if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
@@ -348,14 +374,14 @@
if (mPreparingToUndo) {
mDeleteRunnables.add(r);
} else {
- ((Executor) MODEL_EXECUTOR).execute(r);
+ MODEL_EXECUTOR.execute(r);
}
}
public void commitDelete() {
mPreparingToUndo = false;
for (Runnable runnable : mDeleteRunnables) {
- ((Executor) MODEL_EXECUTOR).execute(runnable);
+ MODEL_EXECUTOR.execute(runnable);
}
mDeleteRunnables.clear();
}
@@ -371,6 +397,20 @@
mModel.forceReload();
}
+ private void notifyOtherCallbacks(CallbackTask task) {
+ if (mOwner == null) {
+ // If the call is happening from a model, it will take care of updating the callbacks
+ return;
+ }
+ mUiExecutor.execute(() -> {
+ for (Callbacks c : mModel.getCallbacks()) {
+ if (c != mOwner) {
+ task.execute(c);
+ }
+ }
+ });
+ }
+
private class UpdateItemRunnable extends UpdateItemBaseRunnable {
private final ItemInfo mItem;
private final Supplier<ContentWriter> mWriter;
@@ -491,7 +531,7 @@
int executeId = mBgDataModel.lastBindId;
- mUiHandler.post(() -> {
+ mUiExecutor.post(() -> {
int currentId = mBgDataModel.lastBindId;
if (currentId > executeId) {
// Model was already bound after job was executed.
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 2f1f82d..2dc644b 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -110,4 +110,5 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String WORK_PROFILE_REMOVED = "b/159671700";
public static final String FALLBACK_ACTIVITY_NO_SET = "b/181019015";
+ public static final String HOME_TO_OVERVIEW_FLAKY = "b/193440212";
}
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 1276ece..cfd3153 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -17,6 +17,8 @@
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS;
+
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
@@ -41,7 +43,8 @@
*/
public abstract class BaseSwipeDetector {
- private static final boolean DBG = false;
+ // b/193440212: Debug swipe gesture in tests.
+ private static final boolean DBG = IS_RUNNING_IN_TEST_HARNESS;
private static final String TAG = "BaseSwipeDetector";
private static final float ANIMATION_DURATION = 1200;
/** The minimum release velocity in pixels per millisecond that triggers fling.*/
diff --git a/src/com/android/launcher3/util/IntSet.java b/src/com/android/launcher3/util/IntSet.java
index 0f4df62..e5b4f59 100644
--- a/src/com/android/launcher3/util/IntSet.java
+++ b/src/com/android/launcher3/util/IntSet.java
@@ -101,6 +101,15 @@
return wrap(IntArray.wrap(array));
}
+ /**
+ * Returns an IntSet with the given values.
+ */
+ public static IntSet wrap(Iterable<Integer> iterable) {
+ IntSet set = new IntSet();
+ iterable.forEach(set::add);
+ return set;
+ }
+
@Override
public Iterator<Integer> iterator() {
return mArray.iterator();
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 354609d..e8ba28f 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -23,6 +23,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -89,4 +90,13 @@
static ItemInfoMatcher ofItemIds(IntSet ids) {
return (info, cn) -> ids.contains(info.id);
}
+
+ /**
+ * Returns a matcher for items with provided items
+ */
+ static ItemInfoMatcher ofItems(Collection<? extends ItemInfo> items) {
+ IntSet ids = new IntSet();
+ items.forEach(item -> ids.add(item.id));
+ return ofItemIds(ids);
+ }
}
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
new file mode 100644
index 0000000..a4cb30a
--- /dev/null
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Interface representing a container which can bind Launcher items with some utility methods
+ */
+public interface LauncherBindableItemsContainer {
+
+ /**
+ * Called to update workspace items as a result of
+ * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindWorkspaceItemsChanged(List)}
+ */
+ default void updateWorkspaceItems(List<WorkspaceItemInfo> shortcuts, ActivityContext context) {
+ final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
+ ItemOperator op = (info, v) -> {
+ if (v instanceof BubbleTextView && updates.contains(info)) {
+ WorkspaceItemInfo si = (WorkspaceItemInfo) info;
+ BubbleTextView shortcut = (BubbleTextView) v;
+ Drawable oldIcon = shortcut.getIcon();
+ boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
+ && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
+ shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
+ } else if (info instanceof FolderInfo && v instanceof FolderIcon) {
+ ((FolderIcon) v).updatePreviewItems(updates::contains);
+ }
+
+ // Iterate all items
+ return false;
+ };
+
+ mapOverItems(op);
+ Folder openFolder = Folder.getOpen(context);
+ if (openFolder != null) {
+ openFolder.iterateOverItems(op);
+ }
+ }
+
+ /**
+ * Called to update restored items as a result of
+ * {@link com.android.launcher3.model.BgDataModel.Callbacks#bindRestoreItemsChange(HashSet)}}
+ */
+ default void updateRestoreItems(final HashSet<ItemInfo> updates, ActivityContext context) {
+ ItemOperator op = (info, v) -> {
+ if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
+ && updates.contains(info)) {
+ ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
+ } else if (v instanceof PendingAppWidgetHostView
+ && info instanceof LauncherAppWidgetInfo
+ && updates.contains(info)) {
+ ((PendingAppWidgetHostView) v).applyState();
+ } else if (v instanceof FolderIcon && info instanceof FolderInfo) {
+ ((FolderIcon) v).updatePreviewItems(updates::contains);
+ }
+ // process all the shortcuts
+ return false;
+ };
+
+ mapOverItems(op);
+ Folder folder = Folder.getOpen(context);
+ if (folder != null) {
+ folder.iterateOverItems(op);
+ }
+ }
+
+ /**
+ * Map the operator over the shortcuts and widgets.
+ *
+ * @param op the operator to map over the shortcuts
+ */
+ void mapOverItems(ItemOperator op);
+
+ interface ItemOperator {
+ /**
+ * Process the next itemInfo, possibly with side-effect on the next item.
+ *
+ * @param info info for the shortcut
+ * @param view view for the shortcut
+ * @return true if done, false to continue the map
+ */
+ boolean evaluate(ItemInfo info, View view);
+ }
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index adc7ba0..ee44174 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -176,7 +176,9 @@
}
PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
- dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview());
+ // RemoteViews are being rendered in AppWidgetHostView in WidgetCell. And thus, the scale of
+ // RemoteViews is equivalent to the AppWidgetHostView scale.
+ dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview(), v.getAppWidgetHostViewScale());
dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
if (image.getDrawable() != null) {
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index cea4de7..a4003d4 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -22,6 +22,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.util.Size;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.RemoteViews;
@@ -41,6 +42,7 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.RoundDrawableWrapper;
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
+import com.android.launcher3.widget.util.WidgetSizes;
/**
* Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -54,6 +56,7 @@
private int[] mEstimatedCellSize;
@Nullable private RemoteViews mRemoteViewsPreview;
+ private float mRemoteViewsPreviewScale = 1f;
@Nullable private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
private final float mEnforcedRoundedCornersForWidget;
@@ -68,8 +71,10 @@
* Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in
* the pin widget flow.
*/
- public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview) {
+ public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview,
+ float previewScale) {
mRemoteViewsPreview = remoteViewsPreview;
+ mRemoteViewsPreviewScale = previewScale;
}
/** Sets a {@link NavigableAppWidgetHostView} which shows a preview layout of an app widget. */
@@ -120,13 +125,14 @@
mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
padding.bottom);
mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview);
- int width =
- deviceProfile.cellWidthPx * mAddInfo.spanX + padding.left + padding.right;
- int height =
- deviceProfile.cellHeightPx * mAddInfo.spanY + padding.top + padding.bottom;
+ Size widgetSizes = WidgetSizes.getWidgetPaddedSizePx(launcher,
+ mAddInfo.componentName, deviceProfile, mAddInfo.spanX, mAddInfo.spanY);
mAppWidgetHostViewPreview.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(widgetSizes.getWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(widgetSizes.getHeight(), MeasureSpec.EXACTLY));
+ mAppWidgetHostViewPreview.setClipChildren(false);
+ mAppWidgetHostViewPreview.setClipToPadding(false);
+ mAppWidgetHostViewPreview.setScaleToFit(mRemoteViewsPreviewScale);
}
if (mAppWidgetHostViewPreview != null) {
previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 5759f75..0c9a15a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,12 +16,14 @@
package com.android.launcher3.widget;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.Utilities.ATLEAST_S;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.CancellationSignal;
import android.util.AttributeSet;
@@ -31,6 +33,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -43,6 +46,7 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.RoundDrawableWrapper;
@@ -71,11 +75,36 @@
/** Widget preview width is calculated by multiplying this factor to the widget cell width. */
private static final float PREVIEW_SCALE = 0.8f;
- protected int mPreviewWidth;
- protected int mPreviewHeight;
+ /**
+ * The maximum dimension that can be used as the size in
+ * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int)}.
+ *
+ * <p>This is equal to (1 << MeasureSpec.MODE_SHIFT) - 1.
+ */
+ private static final int MAX_MEASURE_SPEC_DIMENSION = (1 << 30) - 1;
+
+ /**
+ * The target preview width, in pixels, of a widget or a shortcut.
+ *
+ * <p>The actual preview width may be smaller than or equal to this value subjected to scaling.
+ */
+ protected int mTargetPreviewWidth;
+
+ /**
+ * The target preview height, in pixels, of a widget or a shortcut.
+ *
+ * <p>The actual preview height may be smaller than or equal to this value subjected to scaling.
+ */
+ protected int mTargetPreviewHeight;
+
protected int mPresetPreviewSize;
+
private int mCellSize;
- private float mPreviewScale = 1f;
+
+ /**
+ * The scale of the preview container.
+ */
+ private float mPreviewContainerScale = 1f;
private FrameLayout mWidgetImageContainer;
private WidgetImageView mWidgetImage;
@@ -96,10 +125,10 @@
protected final BaseActivity mActivity;
private final CheckLongPressHelper mLongPressHelper;
private final float mEnforcedCornerRadius;
- private final int mShortcutPreviewPadding;
private RemoteViews mRemoteViewsPreview;
private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
+ private float mAppWidgetHostViewScale = 1f;
private int mSourceContainer = CONTAINER_WIDGETS_TRAY;
public WidgetCell(Context context) {
@@ -122,14 +151,12 @@
setClipToPadding(false);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
- mShortcutPreviewPadding =
- 2 * getResources().getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
}
private void setContainerWidth() {
mCellSize = (int) (mActivity.getDeviceProfile().allAppsIconSizePx * WIDTH_SCALE);
mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
- mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+ mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
}
@Override
@@ -152,6 +179,11 @@
return mRemoteViewsPreview;
}
+ /** Returns the app widget host view scale, which is a value between [0f, 1f]. */
+ public float getAppWidgetHostViewScale() {
+ return mAppWidgetHostViewScale;
+ }
+
/**
* Called to clear the view and free attached resources. (e.g., {@link Bitmap}
*/
@@ -166,7 +198,7 @@
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
mWidgetDescription.setVisibility(GONE);
- mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+ mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
if (mActiveRequest != null) {
mActiveRequest.cancel();
@@ -177,6 +209,7 @@
mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
}
mAppWidgetHostViewPreview = null;
+ mAppWidgetHostViewScale = 1f;
mItem = null;
}
@@ -217,12 +250,7 @@
private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
if (mRemoteViewsPreview != null) {
- mAppWidgetHostViewPreview = new NavigableAppWidgetHostView(getContext()) {
- @Override
- protected boolean shouldAllowDirectClick() {
- return false;
- }
- };
+ mAppWidgetHostViewPreview = createAppWidgetHostView(getContext());
setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
mRemoteViewsPreview);
return;
@@ -230,10 +258,15 @@
if (!item.hasPreviewLayout()) return;
- mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(getContext());
+ Context context = getContext();
+ // If the context is a Launcher activity, DragView will show mAppWidgetHostViewPreview as
+ // a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView, which
+ // supports applying local color extraction during drag & drop.
+ mAppWidgetHostViewPreview = isLauncherContext(context)
+ ? new LauncherAppWidgetHostView(context)
+ : createAppWidgetHostView(context);
LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
- LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(),
- item.widgetInfo.clone());
+ LauncherAppWidgetProviderInfo.fromProviderInfo(context, item.widgetInfo.clone());
// A hack to force the initial layout to be the preview layout since there is no API for
// rendering a preview layout for work profile apps yet. For non-work profile layout, a
// proper solution is to use RemoteViews(PackageName, LayoutId).
@@ -248,16 +281,6 @@
@Nullable RemoteViews remoteViews) {
appWidgetHostViewPreview.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
appWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, providerInfo);
- Rect padding;
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- if (deviceProfile.shouldInsetWidgets()) {
- padding = new Rect();
- appWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
- } else {
- padding = deviceProfile.inv.defaultWidgetPadding;
- }
- appWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
- padding.bottom);
appWidgetHostViewPreview.updateAppWidget(remoteViews);
}
@@ -305,7 +328,7 @@
if (getWidth() > 0 && getHeight() > 0) {
// Scale down the preview size if it's wider than the cell.
float maxWidth = getWidth();
- float previewWidth = drawable.getIntrinsicWidth() * mPreviewScale;
+ float previewWidth = drawable.getIntrinsicWidth() * mPreviewContainerScale;
scale = Math.min(maxWidth / previewWidth, 1);
}
setContainerSize(
@@ -329,16 +352,32 @@
private void setContainerSize(int width, int height) {
LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
- layoutParams.width = (int) (width * mPreviewScale);
- layoutParams.height = (int) (height * mPreviewScale);
+ layoutParams.width = width;
+ layoutParams.height = height;
mWidgetImageContainer.setLayoutParams(layoutParams);
}
public void ensurePreview() {
if (mAppWidgetHostViewPreview != null) {
- setContainerSize(mPreviewWidth, mPreviewHeight);
+ int containerWidth = (int) (mTargetPreviewWidth * mPreviewContainerScale);
+ int containerHeight = (int) (mTargetPreviewHeight * mPreviewContainerScale);
+ setContainerSize(containerWidth, containerHeight);
+ if (mAppWidgetHostViewPreview.getChildCount() == 1) {
+ View widgetContent = mAppWidgetHostViewPreview.getChildAt(0);
+ ViewGroup.LayoutParams layoutParams = widgetContent.getLayoutParams();
+ // We only scale preview if both the width & height of the outermost view group are
+ // not set to MATCH_PARENT.
+ boolean shouldScale =
+ layoutParams.width != MATCH_PARENT && layoutParams.height != MATCH_PARENT;
+ if (shouldScale) {
+ setNoClip(mWidgetImageContainer);
+ setNoClip(mAppWidgetHostViewPreview);
+ mAppWidgetHostViewScale = computeWidgetPreviewScale();
+ mAppWidgetHostViewPreview.setScaleToFit(mAppWidgetHostViewScale);
+ }
+ }
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- mPreviewWidth, mPreviewHeight, Gravity.FILL);
+ containerWidth, containerHeight, Gravity.FILL);
mAppWidgetHostViewPreview.setLayoutParams(params);
mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
mWidgetImage.setVisibility(View.GONE);
@@ -350,7 +389,7 @@
}
mActiveRequest = mWidgetPreviewLoader.loadPreview(
BaseActivity.fromContext(getContext()), mItem,
- new Size(mPreviewWidth, mPreviewHeight),
+ new Size(mTargetPreviewWidth, mTargetPreviewHeight),
this::applyPreview);
}
@@ -363,9 +402,9 @@
public Size setPreviewSize(WidgetItem widgetItem, float previewScale) {
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile, widgetItem);
- mPreviewWidth = widgetSize.getWidth();
- mPreviewHeight = widgetSize.getHeight();
- mPreviewScale = previewScale;
+ mTargetPreviewWidth = widgetSize.getWidth();
+ mTargetPreviewHeight = widgetSize.getHeight();
+ mPreviewContainerScale = previewScale;
return widgetSize;
}
@@ -400,6 +439,24 @@
return "";
}
+ private static NavigableAppWidgetHostView createAppWidgetHostView(Context context) {
+ return new NavigableAppWidgetHostView(context) {
+ @Override
+ protected boolean shouldAllowDirectClick() {
+ return false;
+ }
+ };
+ }
+
+ private static boolean isLauncherContext(Context context) {
+ try {
+ Launcher.getLauncher(context);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
@Override
public CharSequence getAccessibilityClassName() {
return WidgetCell.class.getName();
@@ -410,4 +467,35 @@
super.onInitializeAccessibilityNodeInfo(info);
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
}
+
+ private static void setNoClip(ViewGroup view) {
+ view.setClipChildren(false);
+ view.setClipToPadding(false);
+ }
+
+ private float computeWidgetPreviewScale() {
+ if (mAppWidgetHostViewPreview.getChildCount() != 1) {
+ return 1f;
+ }
+
+ // Measure the largest possible width & height that the app widget wants to display.
+ mAppWidgetHostViewPreview.measure(
+ makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED),
+ makeMeasureSpec(MAX_MEASURE_SPEC_DIMENSION, MeasureSpec.UNSPECIFIED));
+ int appWidgetContentWidth = mAppWidgetHostViewPreview.getChildAt(0).getMeasuredWidth();
+ int appWidgetContentHeight = mAppWidgetHostViewPreview.getChildAt(0).getMeasuredHeight();
+ if (appWidgetContentWidth == 0 || appWidgetContentHeight == 0) {
+ return 1f;
+ }
+
+ int horizontalPadding = mAppWidgetHostViewPreview.getPaddingStart()
+ + mAppWidgetHostViewPreview.getPaddingEnd();
+ int verticalPadding = mAppWidgetHostViewPreview.getPaddingTop()
+ + mAppWidgetHostViewPreview.getPaddingBottom();
+ return Math.min(
+ (mTargetPreviewWidth - horizontalPadding) * mPreviewContainerScale
+ / appWidgetContentWidth,
+ (mTargetPreviewHeight - verticalPadding) * mPreviewContainerScale
+ / appWidgetContentHeight);
+ }
}
diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
index 33066e4..032a7b4 100644
--- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
+++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java
@@ -25,8 +25,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import org.junit.After;
import org.junit.Test;
@@ -75,7 +75,7 @@
@Test
public void testPromiseIcon_addedFromEligibleSession() throws Throwable {
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
- final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ final ItemOperator findPromiseApp = (info, view) ->
info != null && TextUtils.equals(info.title, appLabel);
// Create and add test session
@@ -97,7 +97,7 @@
@Test
public void testPromiseIcon_notAddedFromIneligibleSession() throws Throwable {
final String appLabel = "Test Promise App " + UUID.randomUUID().toString();
- final Workspace.ItemOperator findPromiseApp = (info, view) ->
+ final ItemOperator findPromiseApp = (info, view) ->
info != null && TextUtils.equals(info.title, appLabel);
// Create and add test session without icon or label
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 0c9c463..5ea5d65 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -28,13 +28,13 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.Workspace;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.testcomponent.WidgetConfigActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.Wait.Condition;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -102,8 +102,7 @@
setResult(acceptConfig);
if (acceptConfig) {
- // Dismiss widget resize frame.
- mDevice.pressHome();
+ // TODO(b/192655785) Assert widget resize frame is shown and then dismiss it.
Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
@@ -122,7 +121,7 @@
/**
* Condition for searching widget id
*/
- private class WidgetSearchCondition implements Condition, Workspace.ItemOperator {
+ private class WidgetSearchCondition implements Condition, ItemOperator {
@Override
public boolean isTrue() throws Throwable {
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 745dc22..641e53a 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -30,7 +30,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -40,6 +39,7 @@
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
import com.android.launcher3.testcomponent.RequestPinItemActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.Wait.Condition;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 6a6ec3e..2093682 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -18,7 +18,7 @@
import android.app.Activity;
import com.android.launcher3.Launcher;
-import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 1cb6b2d..78301e4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -135,6 +135,7 @@
.collect(Collectors.toList()),
mLauncher.getVisibleBounds(searchBox).bottom
- mLauncher.getVisibleBounds(allAppsContainer).top);
+ verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
"Scrolled in a wrong direction in AllApps: from " + scroll + " to "
@@ -144,7 +145,6 @@
mLauncher.assertTrue(
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- verifyActiveContainer();
scroll = newScroll;
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 75b87b7..b290bb1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -71,6 +71,10 @@
return mLauncher.isTablet();
}
+ protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
+ return false;
+ }
+
protected void goToOverviewUnchecked() {
switch (mLauncher.getNavigationModel()) {
case ZERO_BUTTON: {
@@ -90,21 +94,32 @@
mLauncher.sendPointer(
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
- mLauncher.executeAndWaitForLauncherEvent(
- () -> mLauncher.movePointer(
- downTime,
- downTime,
- ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
- start,
- end,
- gestureScope),
- event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
- () -> "Pause wasn't detected", "swiping and holding");
- mLauncher.runToState(
- () -> mLauncher.sendPointer(
- downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
- gestureScope),
- OVERVIEW_STATE_ORDINAL, "sending UP event");
+ Runnable swipeAndHold = () -> mLauncher.movePointer(
+ downTime,
+ downTime,
+ ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+ start,
+ end,
+ gestureScope);
+ String swipeAndHoldAction = "swiping and holding";
+ Runnable up = () -> mLauncher.sendPointer(
+ downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
+ gestureScope);
+ String upAction = "sending UP event";
+ if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) {
+ mLauncher.runToState(swipeAndHold, OVERVIEW_STATE_ORDINAL, swipeAndHoldAction);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(upAction)) {
+ up.run();
+ }
+ } else {
+ mLauncher.executeAndWaitForLauncherEvent(
+ swipeAndHold,
+ event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(
+ event.getClassName()),
+ () -> "Pause wasn't detected",
+ swipeAndHoldAction);
+ mLauncher.runToState(up, OVERVIEW_STATE_ORDINAL, upAction);
+ }
break;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 0060844..ee9dd1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -63,4 +63,8 @@
return true;
}
+ @Override
+ protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
+ return true;
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9d6cb4e..c4d46ee 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -505,7 +505,7 @@
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
"http://go/tapl test failure:\nContext: " + getContextDescription()
- + " - visible state is " + getVisibleStateMessage()
+ + " => resulting visible state is " + getVisibleStateMessage()
+ ";\nDetails: " + message, true)));
}
@@ -699,7 +699,8 @@
* @return the Workspace object.
*/
public Workspace pressHome() {
- try (LauncherInstrumentation.Closable e = eventsCheck()) {
+ try (LauncherInstrumentation.Closable e = eventsCheck();
+ LauncherInstrumentation.Closable c = addContextLayer("want to switch to home")) {
waitForLauncherInitialized();
// Click home, then wait for any accessibility event, then wait until accessibility
// events stop.
@@ -719,7 +720,7 @@
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
false, GestureScope.INSIDE_TO_OUTSIDE);
- try (LauncherInstrumentation.Closable c = addContextLayer(
+ try (LauncherInstrumentation.Closable c1 = addContextLayer(
"Swiped up from context menu to home")) {
waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
// Swiping up can temporarily bring Nexus Launcher if the current
@@ -735,6 +736,7 @@
dumpViewHierarchy();
action = "swiping up to home";
+ final boolean launcherIsVisible = isLauncherVisible();
swipeToState(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
@@ -742,6 +744,9 @@
launcherWasVisible
? GestureScope.INSIDE_TO_OUTSIDE
: GestureScope.OUTSIDE_WITH_PILFER);
+ // b/193653850: launcherWasVisible is a flaky indicator.
+ log("launcherWasVisible: " + launcherWasVisible + ", launcherIsVisible: "
+ + launcherIsVisible);
}
} else {
log("Hierarchy before clicking home:");
@@ -764,7 +769,7 @@
|| hasLauncherObject(OVERVIEW_RES_ID)),
action);
}
- try (LauncherInstrumentation.Closable c = addContextLayer(
+ try (LauncherInstrumentation.Closable c1 = addContextLayer(
"performed action to switch to Home - " + action)) {
return getWorkspace();
}