Snap for 7752790 from 5f412199ad21e17b49a20a4d06600b0265106fff to sc-v2-release
Change-Id: Iaa2ee136e63d0ca5593c815fee81a81d88a3ec84
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index d24d752..fcbea77 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -353,6 +353,7 @@
mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations();
+ mAppTransitionManager.registerRemoteTransitions();
internalBindToTIS();
}
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 6afbf9a..5242b3c 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
@TargetApi(Build.VERSION_CODES.P)
public class PredictionRowView extends LinearLayout implements
@@ -170,10 +171,9 @@
private void applyPredictedApps(List<ItemInfo> items) {
mPendingPredictedItems = null;
mPredictedApps.clear();
- items.stream()
+ mPredictedApps.addAll(items.stream()
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
- .map(itemInfo -> (WorkspaceItemInfo) itemInfo)
- .forEach(mPredictedApps::add);
+ .map(itemInfo -> (WorkspaceItemInfo) itemInfo).collect(Collectors.toList()));
applyPredictionApps();
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index a6844e4..63e7390 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -15,12 +15,15 @@
*/
package com.android.launcher3.hybridhotseat;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
- .LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
import android.content.Intent;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
@@ -47,6 +50,8 @@
*/
public class HotseatEduController {
+ private static final String TAG = "HotseatEduController";
+
public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
@@ -188,8 +193,12 @@
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
mNewScreens = IntArray.wrap(pageId);
}
- for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
- View child = mHotseat.getChildAt(i, 0);
+ boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+ int hotseatItemsNum = mLauncher.getDeviceProfile().numShownHotseatIcons;
+ for (int i = 0; i < hotseatItemsNum; i++) {
+ int x = isPortrait ? i : 0;
+ int y = isPortrait ? 0 : hotseatItemsNum - i - 1;
+ View child = mHotseat.getChildAt(x, y);
if (child == null || child.getTag() == null) continue;
ItemInfo tag = (ItemInfo) child.getTag();
if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue;
@@ -229,8 +238,7 @@
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(getSettingsIntent()));
} else {
- new ArrowTipView(mLauncher).show(
- mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+ showHotseatArrowTip(true, mLauncher.getString(R.string.hotseat_tip_no_empty_slots));
}
}
@@ -251,15 +259,50 @@
if (requiresMigration && canMigrateToFirstPage) {
showDialog();
} else {
- new ArrowTipView(mLauncher).show(mLauncher.getString(
+ if (showHotseatArrowTip(requiresMigration, mLauncher.getString(
requiresMigration ? R.string.hotseat_tip_no_empty_slots
- : R.string.hotseat_auto_enrolled),
- mHotseat.getTop());
- mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+ : R.string.hotseat_auto_enrolled))) {
+ mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+ }
finishOnboarding();
}
}
+ /**
+ * Finds a child suitable child in hotseat and shows arrow tip pointing at it.
+ *
+ * @param usePinned used to determine target view. If true, will use the first matching pinned
+ * item. Otherwise, will use the first predicted child
+ * @param message String to be shown inside the arrowView
+ * @return whether suitable child was found and tip was shown
+ */
+ private boolean showHotseatArrowTip(boolean usePinned, String message) {
+ int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
+ boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+
+ BubbleTextView tipTargetView = null;
+ for (int i = childCount - 1; i > -1; i--) {
+ int x = isPortrait ? i : 0;
+ int y = isPortrait ? 0 : i;
+ View v = mHotseat.getShortcutsAndWidgets().getChildAt(x, y);
+ if (v instanceof BubbleTextView && v.getTag() instanceof WorkspaceItemInfo) {
+ ItemInfo info = (ItemInfo) v.getTag();
+ boolean isPinned = info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ if (isPinned == usePinned) {
+ tipTargetView = (BubbleTextView) v;
+ break;
+ }
+ }
+ }
+ if (tipTargetView == null) {
+ Log.e(TAG, "Unable to find suitable view for ArrowTip");
+ return false;
+ }
+ Rect bounds = mLauncher.getViewBounds(tipTargetView);
+ new ArrowTipView(mLauncher).show(message, Gravity.END, bounds.centerX(), bounds.top);
+ return true;
+ }
+
void showDialog() {
if (mPredictedApps == null || mPredictedApps.isEmpty()) {
return;
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index c7c2567..c41f2ce 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -77,6 +77,11 @@
mContent = this;
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ setTranslationShift(TRANSLATION_SHIFT_CLOSED);
+ }
@Override
protected void onFinishInflate() {
@@ -200,9 +205,9 @@
}
AbstractFloatingView.closeAllOpenViews(mActivityContext);
attachToContainer();
- mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
animateOpen();
populatePreview(predictions);
+ mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
}
/**
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 13baf56..85d9f01 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -55,8 +55,8 @@
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.Snackbar;
import java.util.ArrayList;
@@ -152,38 +152,15 @@
*/
public void showEdu() {
mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
- if (mPredictedItems.isEmpty()) {
- // launcher has empty predictions set
- Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
- showDiscoveryTip();
- } else {
- HotseatEduController eduController = new HotseatEduController(mLauncher);
- eduController.setPredictedApps(mPredictedItems.stream()
- .map(i -> (WorkspaceItemInfo) i)
- .collect(Collectors.toList()));
- eduController.showEdu();
- }
+ HotseatEduController eduController = new HotseatEduController(mLauncher);
+ eduController.setPredictedApps(mPredictedItems.stream()
+ .map(i -> (WorkspaceItemInfo) i)
+ .collect(Collectors.toList()));
+ eduController.showEdu();
}));
}
/**
- * Shows educational tip for hotseat if user does not go through Tips app.
- */
- private void showDiscoveryTip() {
- if (getPredictedIcons().isEmpty()) {
- new ArrowTipView(mLauncher).show(
- mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
- } else {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- }
- }
-
- /**
* Returns if hotseat client has predictions
*/
public boolean hasPredictions() {
@@ -497,6 +474,28 @@
.log(LAUNCHER_HOTSEAT_RANKED);
}
+ /**
+ * Called when app/shortcut icon is removed by system. This is used to prune visible stale
+ * predictions while while waiting for AppAPrediction service to send new batch of predictions.
+ *
+ * @param matcher filter matching items that have been removed
+ */
+ public void onModelItemsRemoved(ItemInfoMatcher matcher) {
+ if (mPredictedItems.removeIf(matcher::matchesInfo)) {
+ fillGapsWithPrediction(true);
+ }
+ }
+
+ /**
+ * Called when user completes adding item requiring a config activity to the hotseat
+ */
+ public void onDeferredDrop(int cellX, int cellY) {
+ View child = mHotseat.getChildAt(cellX, cellY);
+ if (child instanceof PredictedAppIcon) {
+ removeIconWithoutNotify((PredictedAppIcon) child);
+ }
+ }
+
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3738dce..9050ddc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
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;
@@ -67,7 +68,9 @@
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
+import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -138,6 +141,15 @@
}
@Override
+ protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+ int cellY, PendingRequestArgs args) {
+ if (container == CONTAINER_HOTSEAT) {
+ mHotseatPredictionController.onDeferredDrop(cellX, cellY);
+ }
+ super.completeAddShortcut(data, container, screenId, cellX, cellY, args);
+ }
+
+ @Override
protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
return new QuickstepAccessibilityDelegate(this);
}
@@ -164,7 +176,11 @@
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
// Only pause is taskbar controller is not present
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
- return super.startActivitySafely(v, intent, item);
+ boolean started = super.startActivitySafely(v, intent, item);
+ if (getTaskbarUIController() == null && !started) {
+ mHotseatPredictionController.setPauseUIUpdate(false);
+ }
+ return started;
}
@Override
@@ -229,6 +245,12 @@
}
@Override
+ public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) {
+ super.bindWorkspaceComponentsRemoved(matcher);
+ mHotseatPredictionController.onModelItemsRemoved(matcher);
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
mHotseatPredictionController.destroy();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6298bb8..9fa0f1a 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -536,9 +536,4 @@
pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
}
}
-
- /** Called when OverviewService is bound to this process */
- void onOverviewServiceBound() {
- // Do nothing
- }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index ae6ea79..b0bd747 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -309,13 +309,6 @@
}
@Override
- void onOverviewServiceBound() {
- final BaseQuickstepLauncher activity = getCreatedActivity();
- if (activity == null) return;
- activity.getAppTransitionManager().registerRemoteTransitions();
- }
-
- @Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
LauncherTaskbarUIController uiController = getTaskbarController();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index aea2d4c..61540d1 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -54,6 +53,8 @@
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import com.android.wm.shell.transition.IShellTransitions;
+import java.util.ArrayList;
+
/**
* Holds the reference to SystemUI.
*/
@@ -81,6 +82,7 @@
private ISplitScreenListener mPendingSplitScreenListener;
private IStartingWindowListener mPendingStartingWindowListener;
private ISmartspaceCallback mPendingSmartspaceCallback;
+ private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -161,6 +163,10 @@
setSmartspaceCallback(mPendingSmartspaceCallback);
mPendingSmartspaceCallback = null;
}
+ for (int i = mPendingRemoteTransitions.size() - 1; i >= 0; --i) {
+ registerRemoteTransition(mPendingRemoteTransitions.get(i));
+ }
+ mPendingRemoteTransitions.clear();
if (mPendingSetNavButtonAlpha != null) {
mPendingSetNavButtonAlpha.run();
@@ -688,6 +694,8 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
+ } else {
+ mPendingRemoteTransitions.add(remoteTransition);
}
}
@@ -699,6 +707,7 @@
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
+ mPendingRemoteTransitions.remove(remoteTransition);
}
//
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 3ce12c5..bb8473b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -179,12 +179,6 @@
smartspaceTransitionController);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
- mDeviceState.runOnUserUnlocked(() -> {
- final BaseActivityInterface ai =
- mOverviewComponentObserver.getActivityInterface();
- if (ai == null) return;
- ai.onOverviewServiceBound();
- });
});
sIsInitialized = true;
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 8ec600d..cb8e7f7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2725,7 +2725,7 @@
if (showAsGrid) {
dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
- if (isFocusedTaskDismissed) {
+ if (isFocusedTaskDismissed && !isSplitSelectionActive()) {
nextFocusedTaskFromTop =
mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
// Pick the next focused task from the preferred row.
@@ -2908,7 +2908,8 @@
} else if (child instanceof TaskView) {
TaskView taskView = (TaskView) child;
if (isFocusedTaskDismissed) {
- if (!isSameGridRow(taskView, nextFocusedTaskView)) {
+ if (nextFocusedTaskView != null &&
+ !isSameGridRow(taskView, nextFocusedTaskView)) {
continue;
}
} else {
@@ -2945,7 +2946,7 @@
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
} else {
float primaryTranslation =
- isFocusedTaskDismissed ? nextFocusedTaskWidth : dismissedTaskWidth;
+ nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? primaryTranslation : -primaryTranslation,
clampToProgress(LINEAR, animationStartProgress,
@@ -3255,7 +3256,7 @@
// Never enough space on phones
return true;
} else if (!mActivity.getDeviceProfile().isLandscape) {
- return false;
+ return true;
}
Rect splitBounds = new Rect();
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5fc0480..bc232a6 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,6 +57,7 @@
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
<dimen name="resize_frame_margin">22dp</dimen>
+ <dimen name="resize_frame_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
<!-- App widget reconfigure button -->
<dimen name="widget_reconfigure_button_corner_radius">14dp</dimen>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ee71146..fef3f8f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -2,6 +2,7 @@
import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+import static com.android.launcher3.CellLayout.SPRING_LOADED_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED;
@@ -9,6 +10,8 @@
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -29,6 +32,7 @@
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
@@ -49,12 +53,14 @@
private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
"launcher.reconfigurable_widget_education_tip_seen";
private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpRect2 = new Rect();
private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1;
private static final int INDEX_RIGHT = 2;
private static final int INDEX_BOTTOM = 3;
+ private static final float MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE = 0.5f;
private final Launcher mLauncher;
private final DragViewStateAnnouncer mStateAnnouncer;
@@ -103,6 +109,16 @@
private final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
+ private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
+
+ /**
+ * In the two panel UI, it is not possible to resize a widget to cross its host
+ * {@link CellLayout}'s sibling. When this happens, we gradually reduce the opacity of the
+ * sibling {@link CellLayout} from 1f to
+ * {@link #MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE}.
+ */
+ private final float mDragAcrossTwoPanelOpacityMargin;
+
private boolean mLeftBorderActive;
private boolean mRightBorderActive;
private boolean mTopBorderActive;
@@ -149,6 +165,10 @@
for (int i = 0; i < HANDLE_COUNT; i++) {
mSystemGestureExclusionRects.add(new Rect());
}
+
+ mDragAcrossTwoPanelOpacityMargin = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.resize_frame_invalid_drag_across_two_panel_opacity_margin);
+ mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
}
@Override
@@ -359,6 +379,37 @@
lp.y = sTmpRect.top;
}
+ // Handle invalid resize across CellLayouts in the two panel UI.
+ if (mCellLayout.getParent() instanceof Workspace) {
+ Workspace workspace = (Workspace) mCellLayout.getParent();
+ CellLayout pairedCellLayout = workspace.getScreenPair(mCellLayout);
+ if (pairedCellLayout != null) {
+ Rect focusedCellLayoutBound = sTmpRect;
+ mDragLayerRelativeCoordinateHelper.viewToRect(mCellLayout, focusedCellLayoutBound);
+ Rect resizeFrameBound = sTmpRect2;
+ findViewById(R.id.widget_resize_frame).getGlobalVisibleRect(resizeFrameBound);
+ float progress = 1f;
+ if (workspace.indexOfChild(pairedCellLayout) < workspace.indexOfChild(mCellLayout)
+ && mDeltaX < 0
+ && resizeFrameBound.left < focusedCellLayoutBound.left) {
+ // Resize from right to left.
+ progress = (mDragAcrossTwoPanelOpacityMargin + mDeltaX)
+ / mDragAcrossTwoPanelOpacityMargin;
+ } else if (workspace.indexOfChild(pairedCellLayout)
+ > workspace.indexOfChild(mCellLayout)
+ && mDeltaX > 0
+ && resizeFrameBound.right > focusedCellLayoutBound.right) {
+ // Resize from left to right.
+ progress = (mDragAcrossTwoPanelOpacityMargin - mDeltaX)
+ / mDragAcrossTwoPanelOpacityMargin;
+ }
+ float alpha = Math.max(MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE, progress);
+ float springLoadedProgress = Math.min(1f, 1f - progress);
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, alpha,
+ springLoadedProgress);
+ }
+ }
+
requestLayout();
}
@@ -515,13 +566,24 @@
}
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ final CellLayout pairedCellLayout;
+ if (mCellLayout.getParent() instanceof Workspace) {
+ Workspace workspace = (Workspace) mCellLayout.getParent();
+ pairedCellLayout = workspace.getScreenPair(mCellLayout);
+ } else {
+ pairedCellLayout = null;
+ }
if (!animate) {
lp.width = newWidth;
lp.height = newHeight;
lp.x = newX;
lp.y = newY;
for (int i = 0; i < HANDLE_COUNT; i++) {
- mDragHandles[i].setAlpha(1.0f);
+ mDragHandles[i].setAlpha(1f);
+ }
+ if (pairedCellLayout != null) {
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+ /* springLoadedProgress= */ 0f);
}
requestLayout();
} else {
@@ -538,6 +600,10 @@
set.play(mFirstFrameAnimatorHelper.addTo(
ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
}
+ if (pairedCellLayout != null) {
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+ /* springLoadedProgress= */ 0f, /* animatorSet= */ set);
+ }
set.setDuration(SNAP_DURATION);
set.start();
}
@@ -624,6 +690,52 @@
}
}
+ private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+ float alpha, float springLoadedProgress) {
+ updateInvalidResizeEffect(cellLayout, pairedCellLayout, alpha,
+ springLoadedProgress, /* animatorSet= */ null);
+ }
+
+ private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+ float alpha, float springLoadedProgress, @Nullable AnimatorSet animatorSet) {
+ int childCount = pairedCellLayout.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = pairedCellLayout.getChildAt(i);
+ if (animatorSet != null) {
+ animatorSet.play(
+ mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(child, ALPHA, alpha)));
+ } else {
+ child.setAlpha(alpha);
+ }
+ }
+ if (animatorSet != null) {
+ animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(cellLayout, SPRING_LOADED_PROGRESS,
+ springLoadedProgress)));
+ animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(pairedCellLayout, SPRING_LOADED_PROGRESS,
+ springLoadedProgress)));
+ } else {
+ cellLayout.setSpringLoadedProgress(springLoadedProgress);
+ pairedCellLayout.setSpringLoadedProgress(springLoadedProgress);
+ }
+
+ boolean shouldShowCellLayoutBorder = springLoadedProgress > 0f;
+ if (animatorSet != null) {
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ }
+ });
+ } else {
+ cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ }
+ }
+
@Override
protected boolean isOfType(int type) {
return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 85dd3b3..8a35185 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1264,7 +1264,7 @@
*
* @param data The intent describing the shortcut.
*/
- private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+ protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
int cellY, PendingRequestArgs args) {
if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
|| args.getPendingIntent().getComponent() == null) {
@@ -2128,7 +2128,7 @@
actualIds.add(id);
}
int firstId = visibleIds.getArray().get(0);
- int pairId = mWorkspace.getPagePair(firstId);
+ int pairId = mWorkspace.getScreenPair(firstId);
// Double check that actual screenIds contains the visibleId, as empty screens are hidden
// in single panel.
if (actualIds.contains(firstId)) {
@@ -2212,7 +2212,7 @@
// Some empty pages might have been removed while the phone was in a single panel
// mode, so we want to add those empty pages back.
IntSet screenIds = IntSet.wrap(orderedScreenIds);
- orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getPagePair(screenId)));
+ orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getScreenPair(screenId)));
orderedScreenIds = screenIds.getArray();
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a7198a8..d534c5d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -208,7 +208,7 @@
public void initParentViews(View parent) {
if (mPageIndicatorViewId > -1) {
mPageIndicator = parent.findViewById(mPageIndicatorViewId);
- mPageIndicator.setMarkersCount(getChildCount());
+ mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
}
}
@@ -830,7 +830,7 @@
private void dispatchPageCountChanged() {
if (mPageIndicator != null) {
- mPageIndicator.setMarkersCount(getChildCount());
+ mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
}
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index bd2a14f..3faa3d0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -651,7 +651,7 @@
// If the icon was dragged from Hotseat, there is no page pair
if (isTwoPanelEnabled() && !(mDragSourceInternal.getParent() instanceof Hotseat)) {
- int pagePairScreenId = getPagePair(dragObject.dragInfo.screenId);
+ int pagePairScreenId = getScreenPair(dragObject.dragInfo.screenId);
CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId);
if (pagePair == null) {
// TODO: after http://b/198820019 is fixed, remove this
@@ -917,16 +917,33 @@
}
/**
- * Returns the page that is shown together with the given page when two panel is enabled.
+ * Returns the screen ID of a page that is shown together with the given page screen ID when the
+ * two panel UI is enabled.
*/
- public int getPagePair(int page) {
- if (page % 2 == 0) {
- return page + 1;
+ public int getScreenPair(int screenId) {
+ if (screenId % 2 == 0) {
+ return screenId + 1;
} else {
- return page - 1;
+ return screenId - 1;
}
}
+ /**
+ * Returns {@link CellLayout} that is shown together with the given {@link CellLayout} when the
+ * two panel UI is enabled.
+ */
+ @Nullable
+ public CellLayout getScreenPair(CellLayout cellLayout) {
+ if (!isTwoPanelEnabled()) {
+ return null;
+ }
+ int screenId = getIdForScreen(cellLayout);
+ if (screenId == -1) {
+ return null;
+ }
+ return getScreenWithId(getScreenPair(screenId));
+ }
+
public void stripEmptyScreens() {
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading.
@@ -959,7 +976,7 @@
Iterator<Integer> removeScreensIterator = removeScreens.iterator();
while (removeScreensIterator.hasNext()) {
int pageToRemove = removeScreensIterator.next();
- int pagePair = getPagePair(pageToRemove);
+ int pagePair = getScreenPair(pageToRemove);
if (!removeScreens.contains(pagePair)) {
// The page pair isn't empty so we want to remove the current page from the
// removable pages' collection
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2032b26..157df5d 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -162,7 +162,8 @@
}
}
- if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+ if ((item instanceof AppInfo) || (item instanceof WorkspaceItemInfo)
+ || (item instanceof PendingAddItemInfo)) {
out.add(mActions.get(ADD_TO_WORKSPACE));
}
}
@@ -244,6 +245,13 @@
mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
+ else if (item instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone();
+ mLauncher.getModelWriter().addItemToDatabase(info,
+ Favorites.CONTAINER_DESKTOP,
+ screenId, coordinates[0], coordinates[1]);
+ mLauncher.bindItems(Collections.singletonList(info), true, true);
+ }
}));
return true;
} else if (action == MOVE_TO_WORKSPACE) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index ee5f7e4..3681704 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -714,6 +714,7 @@
@Override
public void drawOnScrim(Canvas canvas) {
+ if (!mHeader.isHeaderProtectionSupported()) return;
mHeaderPaint.setColor(mHeaderColor);
mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a6d8552..fc78bea 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -39,7 +39,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
@@ -61,7 +60,6 @@
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
- private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -188,8 +186,7 @@
int visibleElements = state.getVisibleElements(mLauncher);
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
- Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE,
- Interpolators.clampToProgress(LINEAR, 0, CONTENT_VISIBLE_MAX_THRESHOLD));
+ Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
boolean shouldProtectHeader =
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 690e904..a395709 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -204,7 +204,7 @@
}
@Override
- public ItemInfoWithIcon clone() {
+ public WorkspaceItemInfo clone() {
return new WorkspaceItemInfo(this);
}
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index bc3419a..454dc6e 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -480,12 +480,12 @@
@Override
protected void closeComplete() {
+ super.closeComplete();
PopupContainerWithArrow openPopup = getOpen(mLauncher);
if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.setForceHideDot(false);
}
- super.closeComplete();
}
/**
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index ab2652a..4894b3b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
+/*
+ * Copyright (C) 2015 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.
@@ -17,18 +17,31 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
import android.view.MotionEvent;
+import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.StateAnimationConfig;
/**
* TouchController to switch between NORMAL and ALL_APPS state.
*/
public class AllAppsSwipeController extends AbstractStateChangeTouchController {
+ private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f;
+
+ public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER =
+ Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD);
+ public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER =
+ Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f);
+
public AllAppsSwipeController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
}
@@ -65,12 +78,28 @@
@Override
protected float initCurrentAnimation() {
float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
+ StateAnimationConfig config = getConfigForStates(mFromState, mToState);
+ config.duration = (long) (2 * range);
+
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, maxAccuracy);
+ .createAnimationToNewWorkspace(mToState, config);
float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
float totalShift = endVerticalShift - startVerticalShift;
return 1 / totalShift;
}
+
+ @Override
+ protected StateAnimationConfig getConfigForStates(LauncherState fromState,
+ LauncherState toState) {
+ StateAnimationConfig config = super.getConfigForStates(fromState, toState);
+ if (fromState == NORMAL && toState == ALL_APPS) {
+ config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+ }
+ return config;
+ }
}
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index e449a4b..ce26a66 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -37,6 +37,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
@@ -56,6 +57,7 @@
protected final BaseDraggingActivity mActivity;
private final Handler mHandler = new Handler();
private final int mArrowWidth;
+ private final int mArrowMinOffset;
private boolean mIsPointingUp;
private Runnable mOnClosed;
private View mArrowView;
@@ -69,6 +71,8 @@
mActivity = BaseDraggingActivity.fromContext(context);
mIsPointingUp = isPointingUp;
mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
+ mArrowMinOffset = context.getResources().getDimensionPixelSize(
+ R.dimen.dynamic_grid_cell_border_spacing);
init(context);
}
@@ -126,10 +130,10 @@
/**
* Show the ArrowTipView (tooltip) center, start, or end aligned.
*
- * @param text The text to be shown in the tooltip.
- * @param gravity The gravity aligns the tooltip center, start, or end.
+ * @param text The text to be shown in the tooltip.
+ * @param gravity The gravity aligns the tooltip center, start, or end.
* @param arrowMarginStart The margin from start to place arrow (ignored if center)
- * @param top The Y coordinate of the bottom of tooltip.
+ * @param top The Y coordinate of the bottom of tooltip.
* @return The tooltip.
*/
public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
@@ -137,23 +141,28 @@
ViewGroup parent = mActivity.getDragLayer();
parent.addView(this);
+ DeviceProfile grid = mActivity.getDeviceProfile();
+
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = gravity;
+ params.leftMargin = mArrowMinOffset + grid.getInsets().left;
+ params.rightMargin = mArrowMinOffset + grid.getInsets().right;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams();
+
lp.gravity = gravity;
if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
arrowMarginStart = parent.getMeasuredWidth() - arrowMarginStart;
}
if (gravity == Gravity.END) {
- lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth);
+ lp.setMarginEnd(Math.max(mArrowMinOffset,
+ parent.getMeasuredWidth() - params.rightMargin - arrowMarginStart
+ - mArrowWidth / 2));
} else if (gravity == Gravity.START) {
- lp.setMarginStart(arrowMarginStart - mArrowWidth / 2);
+ lp.setMarginStart(Math.max(mArrowMinOffset,
+ arrowMarginStart - params.leftMargin - mArrowWidth / 2));
}
requestLayout();
-
- params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
- params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
mIsOpen = true;