Merge "Fix recents scale sometimes lagging behind window scale" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 8db875b..555cc73 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -169,7 +169,7 @@
<activity
android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text"
- android:theme="@android:style/Theme.DeviceDefault.Settings"
+ android:theme="@style/HomeSettingsTheme"
android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
diff --git a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
index e7099ec..3842efb 100644
--- a/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ b/go/quickstep/src/com/android/quickstep/util/ShelfPeekAnim.java
@@ -24,4 +24,8 @@
public enum ShelfAnimState {
}
+
+ public boolean isPeeking() {
+ return false;
+ }
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index 535c5d8..8bd9dba 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -44,6 +44,7 @@
private final PackageManager mPm;
private final ColorExtractor mColorExtractor;
private boolean mDisableColorExtractor;
+ private boolean mBadgeOnLeft = false;
protected final int mFillResIconDpi;
protected final int mIconBitmapSize;
@@ -77,6 +78,7 @@
protected void clear() {
mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
mDisableColorExtractor = false;
+ mBadgeOnLeft = false;
}
public ShadowGenerator getShadowGenerator() {
@@ -201,6 +203,13 @@
}
/**
+ * Switches badging to left/right
+ */
+ public void setBadgeOnLeft(boolean badgeOnLeft) {
+ mBadgeOnLeft = badgeOnLeft;
+ }
+
+ /**
* Sets the background color used for wrapped adaptive icon
*/
public void setWrapperBackgroundColor(int color) {
@@ -261,8 +270,12 @@
*/
public void badgeWithDrawable(Canvas target, Drawable badge) {
int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize);
- badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
- mIconBitmapSize, mIconBitmapSize);
+ if (mBadgeOnLeft) {
+ badge.setBounds(0, mIconBitmapSize - badgeSize, badgeSize, mIconBitmapSize);
+ } else {
+ badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
+ mIconBitmapSize, mIconBitmapSize);
+ }
badge.draw(target);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
index 424333c..fe9fd60 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java
@@ -23,7 +23,10 @@
import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
import android.content.ComponentName;
+import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -60,9 +63,12 @@
private static final String TAG = "PredictiveHotseat";
private static final boolean DEBUG = false;
+ //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
+ private static final int APPTARGET_ACTION_UNPIN = 4;
+
private static final String PREDICTION_CLIENT = "hotseat";
- private boolean mDragStarted = false;
+ private DropTarget.DragObject mDragObject;
private int mHotSeatItemsCount;
private Launcher mLauncher;
@@ -84,7 +90,6 @@
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
launcher.getDeviceProfile().inv.addOnChangeListener(this);
mHotseat.addOnAttachStateChangeListener(this);
- createPredictor();
}
@Override
@@ -101,7 +106,7 @@
* Fills gaps in the hotseat with predictions
*/
public void fillGapsWithPrediction(boolean animate) {
- if (mDragStarted) {
+ if (mDragObject != null) {
return;
}
List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
@@ -155,7 +160,10 @@
}
}
- private void createPredictor() {
+ /**
+ * Creates App Predictor with all the current apps pinned on the hotseat
+ */
+ public void createPredictor() {
AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
if (apm == null) {
return;
@@ -167,12 +175,28 @@
new AppPredictionContext.Builder(mLauncher)
.setUiSurface(PREDICTION_CLIENT)
.setPredictedTargetCount(mHotSeatItemsCount)
+ .setExtras(getAppPredictionContextExtra())
.build());
mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
this::setPredictedApps);
mAppPredictor.requestPredictionUpdate();
}
+ private Bundle getAppPredictionContextExtra() {
+ Bundle bundle = new Bundle();
+ ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+ ArrayList<AppTarget> pinnedApps = new ArrayList<>();
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ View child = vg.getChildAt(i);
+ if (isPinnedIcon(child)) {
+ WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
+ pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
+ }
+ }
+ bundle.putParcelableArrayList("pinned_apps", pinnedApps);
+ return bundle;
+ }
+
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
for (AppTarget appTarget : appTargets) {
@@ -209,6 +233,8 @@
icon.reset();
icon.applyFromWorkspaceItem(workspaceItemInfo);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+ AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
+ notifyItemAction(appTarget, AppTargetEvent.ACTION_PIN);
}
private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
@@ -270,18 +296,32 @@
}
}
+ private void notifyItemAction(AppTarget target, int action) {
+ if (mAppPredictor != null) {
+ mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target, action).build());
+ }
+ }
+
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
removePredictedApps(true);
- mDragStarted = true;
+ mDragObject = dragObject;
}
@Override
public void onDragEnd() {
- if (!mDragStarted) {
+ if (mDragObject == null) {
return;
}
- mDragStarted = false;
+ ItemInfo dragInfo = mDragObject.dragInfo;
+ if (dragInfo instanceof WorkspaceItemInfo && dragInfo.getTargetComponent() != null) {
+ if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
+ notifyItemAction(getAppTargetFromItemInfo(dragInfo), AppTargetEvent.ACTION_PIN);
+ } else if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
+ notifyItemAction(getAppTargetFromItemInfo(dragInfo), APPTARGET_ACTION_UNPIN);
+ }
+ }
+ mDragObject = null;
fillGapsWithPrediction(true);
}
@@ -339,4 +379,26 @@
&& ((WorkspaceItemInfo) view.getTag()).container
== LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
}
+
+ private static boolean isPinnedIcon(View view) {
+ if (!(view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo)) {
+ return false;
+ }
+ ItemInfo info = (ItemInfo) view.getTag();
+ return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && (
+ info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+ }
+
+ private static boolean isInHotseat(ItemInfo itemInfo) {
+ return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
+ private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
+ if (info.getTargetComponent() == null) return null;
+ return new AppTarget.Builder(
+ new AppTargetId("app:" + info.getTargetComponent().getPackageName()),
+ info.getTargetComponent().getPackageName(), info.user).setClassName(
+ info.getTargetComponent().getClassName()).build();
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 6946508..51ee216 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -21,8 +21,15 @@
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
@@ -40,6 +47,7 @@
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.quickstep.util.AppWindowAnimationHelper;
@@ -56,6 +64,9 @@
public static final int INDEX_SHELF_ANIM = 0;
public static final int INDEX_RECENTS_FADE_ANIM = 1;
public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
+ public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3;
+
+ public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
public LauncherAppTransitionManagerImpl(Context context) {
super(context);
@@ -145,7 +156,7 @@
@Override
public int getStateElementAnimationsCount() {
- return 3;
+ return 4;
}
@Override
@@ -191,6 +202,20 @@
.setStiffness(250)
.setValues(values)
.build(mLauncher);
+ case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
+ if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+ builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
+ builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
+ }
+ LauncherStateManager stateManager = mLauncher.getStateManager();
+ return stateManager.createAtomicAnimation(
+ stateManager.getCurrentStableState(), OVERVIEW, builder,
+ ANIM_ALL, ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW);
+ }
+
default:
return super.createStateElementAnimation(index, values);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index eefb7dc..6aaae4c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -37,6 +37,7 @@
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
@@ -195,7 +196,7 @@
public void finishBindingItems(int pageBoundFirst) {
super.finishBindingItems(pageBoundFirst);
if (mHotseatPredictionController != null) {
- mHotseatPredictionController.fillGapsWithPrediction(false);
+ mHotseatPredictionController.createPredictor();
}
}
@@ -214,7 +215,7 @@
ArrayList<TouchController> list = new ArrayList<>();
list.add(getDragController());
if (mode == NO_BUTTON) {
- list.add(new QuickSwitchTouchController(this));
+ list.add(new NoButtonQuickSwitchTouchController(this));
list.add(new NavBarToHomeTouchController(this));
list.add(new FlingAndHoldTouchController(this));
} else {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 25eaab1..ed5dba1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
@@ -205,6 +206,7 @@
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+ builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_7);
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 626292e..d388f49 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -16,25 +16,20 @@
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -46,12 +41,13 @@
import android.view.ViewConfiguration;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppTransitionManagerImpl;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.SystemUiProxy;
import com.android.launcher3.util.VibratorWrapper;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.views.RecentsView;
@@ -79,7 +75,7 @@
@Override
protected long getAtomicDuration() {
- return 300;
+ return LauncherAppTransitionManagerImpl.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
}
@Override
@@ -179,15 +175,8 @@
mPeekAnim.cancel();
}
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
- if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
- builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
- }
- AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
- NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
+ Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
+ INDEX_PAUSE_TO_OVERVIEW_ANIM);
overviewAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
new file mode 100644
index 0000000..54d165f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2019 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.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
+import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
+import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
+import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL;
+import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
+import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.touch.BaseSwipeDetector;
+import com.android.launcher3.touch.BothAxesSwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.VibratorWrapper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.ShelfPeekAnim;
+import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.views.LauncherRecentsView;
+
+/**
+ * Handles quick switching to a recent task from the home screen. To give as much flexibility to
+ * the user as possible, also handles swipe up and hold to go to overview and swiping back home.
+ */
+public class NoButtonQuickSwitchTouchController implements TouchController,
+ BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
+
+ /** The minimum progress of the scale/translationY animation until drag end. */
+ private static final float Y_ANIM_MIN_PROGRESS = 0.15f;
+ private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5;
+ private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
+ private static final Interpolator SCALE_DOWN_INTERPOLATOR = DEACCEL;
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final BothAxesSwipeDetector mSwipeDetector;
+ private final ShelfPeekAnim mShelfPeekAnim;
+ private final float mXRange;
+ private final float mYRange;
+ private final MotionPauseDetector mMotionPauseDetector;
+ private final float mMotionPauseMinDisplacement;
+
+ private boolean mNoIntercept;
+ private LauncherState mStartState;
+
+ private boolean mIsHomeScreenVisible = true;
+
+ // As we drag, we control 3 animations: one to get non-overview components out of the way,
+ // and the other two to set overview properties based on x and y progress.
+ private AnimatorPlaybackController mNonOverviewAnim;
+ private AnimatorPlaybackController mXOverviewAnim;
+ private AnimatorPlaybackController mYOverviewAnim;
+
+ public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
+ mShelfPeekAnim = mLauncher.getShelfPeekAnim();
+ mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
+ mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile());
+ mMotionPauseDetector = new MotionPauseDetector(mLauncher);
+ mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
+ R.dimen.motion_pause_detector_min_displacement_from_app);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch(ev);
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Only detect horizontal swipe for intercept, then we will allow swipe up as well.
+ mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT,
+ false /* ignoreSlopWhenSettling */);
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mSwipeDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mSwipeDetector.onTouchEvent(ev);
+ }
+
+ private boolean canInterceptTouch(MotionEvent ev) {
+ if (!mLauncher.isInState(LauncherState.NORMAL)) {
+ return false;
+ }
+ if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
+ return false;
+ }
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+ if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ mMotionPauseDetector.clear();
+ if (start) {
+ mStartState = mLauncher.getStateManager().getState();
+
+ mMotionPauseDetector.setOnMotionPauseListener(this);
+
+ // We have detected horizontal drag start, now allow swipe up as well.
+ mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
+ false /* ignoreSlopWhenSettling */);
+
+ setupAnimators();
+ }
+ }
+
+ @Override
+ public void onMotionPauseChanged(boolean isPaused) {
+ ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
+ if (shelfState == PEEK) {
+ // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+ allAppsController.setAlphas(NORMAL.getVisibleElements(mLauncher),
+ new AnimationConfig(), builder);
+ builder.build().setDuration(0).start();
+
+ if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+ // Hotseat was hidden, but we need it visible when peeking.
+ mLauncher.getHotseat().setAlpha(1);
+ }
+ }
+ mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
+ ShelfPeekAnim.DURATION);
+ VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
+ }
+
+ private void setupAnimators() {
+ // Animate the non-overview components (e.g. workspace, shelf) out of the way.
+ AnimatorSetBuilder nonOverviewBuilder = new AnimatorSetBuilder();
+ nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
+ nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
+ nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
+ nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
+ updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder, ANIM_ALL);
+ mNonOverviewAnim.dispatchOnStart();
+
+ setupOverviewAnimators();
+ }
+
+ /** Create state animation to control non-overview components. */
+ private void updateNonOverviewAnim(LauncherState toState, AnimatorSetBuilder builder,
+ @LauncherStateManager.AnimationComponents int animComponents) {
+ builder.addFlag(FLAG_DONT_ANIMATE_OVERVIEW);
+ long accuracy = (long) (Math.max(mXRange, mYRange) * 2);
+ mNonOverviewAnim = mLauncher.getStateManager().createAnimationToNewWorkspace(toState,
+ builder, accuracy, this::clearState, animComponents);
+ }
+
+ private void setupOverviewAnimators() {
+ final LauncherState fromState = QUICK_SWITCH;
+ final LauncherState toState = OVERVIEW;
+ LauncherState.ScaleAndTranslation fromScaleAndTranslation = fromState
+ .getOverviewScaleAndTranslation(mLauncher);
+ LauncherState.ScaleAndTranslation toScaleAndTranslation = toState
+ .getOverviewScaleAndTranslation(mLauncher);
+ // Update RecentView's translationX to have it start offscreen.
+ LauncherRecentsView recentsView = mLauncher.getOverviewPanel();
+ float startScale = Utilities.mapRange(
+ SCALE_DOWN_INTERPOLATOR.getInterpolation(Y_ANIM_MIN_PROGRESS),
+ fromScaleAndTranslation.scale,
+ toScaleAndTranslation.scale);
+ fromScaleAndTranslation.translationX = recentsView.getOffscreenTranslationX(startScale);
+
+ // Set RecentView's initial properties.
+ recentsView.setScaleX(fromScaleAndTranslation.scale);
+ recentsView.setScaleY(fromScaleAndTranslation.scale);
+ recentsView.setTranslationX(fromScaleAndTranslation.translationX);
+ recentsView.setTranslationY(fromScaleAndTranslation.translationY);
+ recentsView.setContentAlpha(1);
+
+ // As we drag right, animate the following properties:
+ // - RecentsView translationX
+ // - OverviewScrim
+ AnimatorSet xOverviewAnim = new AnimatorSet();
+ xOverviewAnim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X,
+ toScaleAndTranslation.translationX));
+ xOverviewAnim.play(ObjectAnimator.ofFloat(
+ mLauncher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
+ toState.getOverviewScrimAlpha(mLauncher)));
+ long xAccuracy = (long) (mXRange * 2);
+ xOverviewAnim.setDuration(xAccuracy);
+ mXOverviewAnim = AnimatorPlaybackController.wrap(xOverviewAnim, xAccuracy);
+ mXOverviewAnim.dispatchOnStart();
+
+ // As we drag up, animate the following properties:
+ // - RecentsView translationY
+ // - RecentsView scale
+ // - RecentsView fullscreenProgress
+ AnimatorSet yAnimation = new AnimatorSet();
+ Animator translateYAnim = ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_Y,
+ toScaleAndTranslation.translationY);
+ Animator scaleAnim = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY,
+ toScaleAndTranslation.scale);
+ Animator fullscreenProgressAnim = ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS,
+ fromState.getOverviewFullscreenProgress(), toState.getOverviewFullscreenProgress());
+ scaleAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR);
+ fullscreenProgressAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR);
+ yAnimation.play(translateYAnim);
+ yAnimation.play(scaleAnim);
+ yAnimation.play(fullscreenProgressAnim);
+ long yAccuracy = (long) (mYRange * 2);
+ yAnimation.setDuration(yAccuracy);
+ mYOverviewAnim = AnimatorPlaybackController.wrap(yAnimation, yAccuracy);
+ mYOverviewAnim.dispatchOnStart();
+ }
+
+ @Override
+ public boolean onDrag(PointF displacement, MotionEvent ev) {
+ float xProgress = Math.max(0, displacement.x) / mXRange;
+ float yProgress = Math.max(0, -displacement.y) / mYRange;
+ yProgress = Utilities.mapRange(yProgress, Y_ANIM_MIN_PROGRESS, 1f);
+
+ boolean wasHomeScreenVisible = mIsHomeScreenVisible;
+ if (wasHomeScreenVisible && mNonOverviewAnim != null) {
+ mNonOverviewAnim.setPlayFraction(xProgress);
+ }
+ mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress)
+ <= 1 - ALPHA_CUTOFF_THRESHOLD;
+
+ if (wasHomeScreenVisible && !mIsHomeScreenVisible) {
+ // Get the shelf all the way offscreen so it pops up when we decide to peek it.
+ mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0);
+ }
+
+ // Only allow motion pause if the home screen is invisible, since some
+ // home screen elements will appear in the shelf on motion pause.
+ mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
+ || -displacement.y < mMotionPauseMinDisplacement);
+ mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
+
+ if (mIsHomeScreenVisible) {
+ // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
+ mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0);
+ }
+
+ if (mXOverviewAnim != null) {
+ mXOverviewAnim.setPlayFraction(xProgress);
+ }
+ if (mYOverviewAnim != null) {
+ mYOverviewAnim.setPlayFraction(yProgress);
+ }
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(PointF velocity) {
+ boolean horizontalFling = mSwipeDetector.isFling(velocity.x);
+ boolean verticalFling = mSwipeDetector.isFling(velocity.y);
+ boolean noFling = !horizontalFling && !verticalFling;
+ int logAction = noFling ? Touch.SWIPE : Touch.FLING;
+ if (mMotionPauseDetector.isPaused() && noFling) {
+ cancelAnimations();
+
+ Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
+ INDEX_PAUSE_TO_OVERVIEW_ANIM);
+ overviewAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onAnimationToStateCompleted(OVERVIEW, logAction);
+ }
+ });
+ overviewAnim.start();
+ return;
+ }
+
+ final LauncherState targetState;
+ if (horizontalFling && verticalFling) {
+ // Flinging left and up, left and down, or right and up all go back home.
+ // Only flinging right and down goes to quick switch.
+ targetState = velocity.x < 0 || velocity.y < 0 ? NORMAL : QUICK_SWITCH;
+ } else if (horizontalFling) {
+ targetState = velocity.x > 0 ? QUICK_SWITCH : NORMAL;
+ } else if (verticalFling) {
+ targetState = velocity.y > 0 ? QUICK_SWITCH : NORMAL;
+ } else {
+ // If user isn't flinging, just snap to the closest state based on x progress.
+ boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f;
+ targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL;
+ }
+
+ // Animate the various components to the target state.
+
+ float xProgress = mXOverviewAnim.getProgressFraction();
+ float startXProgress = Utilities.boundToRange(xProgress
+ + velocity.x * getSingleFrameMs(mLauncher) / mXRange, 0f, 1f);
+ final float endXProgress = targetState == NORMAL ? 0 : 1;
+ long xDuration = BaseSwipeDetector.calculateDuration(velocity.x,
+ Math.abs(endXProgress - startXProgress));
+ ValueAnimator xOverviewAnim = mXOverviewAnim.getAnimationPlayer();
+ xOverviewAnim.setFloatValues(startXProgress, endXProgress);
+ xOverviewAnim.setDuration(xDuration)
+ .setInterpolator(scrollInterpolatorForVelocity(velocity.x));
+ mXOverviewAnim.dispatchOnStartWithVelocity(endXProgress, velocity.x);
+
+ boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL;
+
+ float yProgress = mYOverviewAnim.getProgressFraction();
+ float startYProgress = Utilities.boundToRange(yProgress
+ - velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f);
+ final float endYProgress;
+ if (flingUpToNormal) {
+ endYProgress = 1;
+ } else if (targetState == NORMAL) {
+ // Keep overview at its current scale/translationY as it slides off the screen.
+ endYProgress = startYProgress;
+ } else {
+ endYProgress = 0;
+ }
+ long yDuration = BaseSwipeDetector.calculateDuration(velocity.y,
+ Math.abs(endYProgress - startYProgress));
+ ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer();
+ yOverviewAnim.setFloatValues(startYProgress, endYProgress);
+ yOverviewAnim.setDuration(yDuration);
+ mYOverviewAnim.dispatchOnStartWithVelocity(endYProgress, velocity.y);
+
+ ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
+ if (flingUpToNormal && !mIsHomeScreenVisible) {
+ // We are flinging to home while workspace is invisible, run the same staggered
+ // animation as from an app.
+ // Update mNonOverviewAnim to do nothing so it doesn't interfere.
+ updateNonOverviewAnim(targetState, new AnimatorSetBuilder(), 0 /* animComponents */);
+ nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
+
+ new StaggeredWorkspaceAnim(mLauncher, null, velocity.y,
+ false /* animateOverviewScrim */).start();
+ } else {
+ boolean canceled = targetState == NORMAL;
+ if (canceled) {
+ // Let the state manager know that the animation didn't go to the target state,
+ // but don't clean up yet (we already clean up when the animation completes).
+ mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable();
+ }
+ float startProgress = mNonOverviewAnim.getProgressFraction();
+ float endProgress = canceled ? 0 : 1;
+ nonOverviewAnim.setFloatValues(startProgress, endProgress);
+ mNonOverviewAnim.dispatchOnStartWithVelocity(endProgress,
+ horizontalFling ? velocity.x : velocity.y);
+ }
+
+ nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
+ mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction));
+
+ cancelAnimations();
+ xOverviewAnim.start();
+ yOverviewAnim.start();
+ nonOverviewAnim.start();
+ }
+
+ private void onAnimationToStateCompleted(LauncherState targetState, int logAction) {
+ mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
+ getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
+ LauncherLogProto.ContainerType.NAVBAR,
+ mStartState.containerType,
+ targetState.containerType,
+ mLauncher.getWorkspace().getCurrentPage());
+ mLauncher.getStateManager().goToState(targetState, false, this::clearState);
+ }
+
+ private int getDirectionForLog() {
+ return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
+ }
+
+ private void cancelAnimations() {
+ if (mNonOverviewAnim != null) {
+ mNonOverviewAnim.getAnimationPlayer().cancel();
+ }
+ if (mXOverviewAnim != null) {
+ mXOverviewAnim.getAnimationPlayer().cancel();
+ }
+ if (mYOverviewAnim != null) {
+ mYOverviewAnim.getAnimationPlayer().cancel();
+ }
+ mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
+ mMotionPauseDetector.clear();
+ }
+
+ private void clearState() {
+ cancelAnimations();
+ mNonOverviewAnim = null;
+ mXOverviewAnim = null;
+ mYOverviewAnim = null;
+ mIsHomeScreenVisible = true;
+ mSwipeDetector.finishedScrolling();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 4f50e33..b14da5c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -48,12 +48,12 @@
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -285,12 +285,18 @@
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
mRecentsAnimationController = null;
mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
mRecentsAnimationController = null;
mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
}
private Rect getStackBounds(DeviceProfile dp) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 24f247b..c939de8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -19,10 +19,10 @@
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
+import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -33,8 +33,8 @@
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
-
import android.util.ArrayMap;
+
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -428,7 +428,6 @@
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
super.onRecentsAnimationCanceled(thumbnailData);
- mRecentsView.setRecentsAnimationTargets(null, null);
mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 844152b..5b6f7fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -24,7 +24,6 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.INSTANT;
@@ -52,10 +51,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
@@ -194,18 +191,8 @@
@Override
public void playAtomicAnimation(float velocity) {
- // Setup workspace with 0 duration to prepare for our staggered animation.
- LauncherStateManager stateManager = launcher.getStateManager();
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- // setRecentsAttachedToAppWindow() will animate recents out.
- builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
- stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0);
- builder.build().start();
-
- // Stop scrolling so that it doesn't interfere with the translation offscreen.
- recentsView.getScroller().forceFinished(true);
-
- new StaggeredWorkspaceAnim(launcher, workspaceView, velocity).start();
+ new StaggeredWorkspaceAnim(launcher, workspaceView, velocity,
+ true /* animateOverviewScrim */).start();
}
};
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index fe3926a..c28bf28 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -611,9 +611,6 @@
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
super.onRecentsAnimationCanceled(thumbnailData);
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
mActivityInitListener.unregister();
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 7b8ec4d..e0e20ee 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -89,9 +89,13 @@
int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0);
IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL);
if (taskID != 0 && thumbnail instanceof ObjectWrapper) {
- ThumbnailData thumbnailData = ((ObjectWrapper<ThumbnailData>) thumbnail).get();
+ ObjectWrapper<ThumbnailData> obj = (ObjectWrapper<ThumbnailData>) thumbnail;
+ ThumbnailData thumbnailData = obj.get();
mFallbackRecentsView.showCurrentTask(taskID);
mFallbackRecentsView.updateThumbnail(taskID, thumbnailData);
+ // Clear the ref since any reference to the extras on the system side will still
+ // hold a reference to the wrapper
+ obj.clear();
}
}
intent.removeExtra(EXTRA_TASK_ID);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 2522c0f..bfe5738 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -172,8 +172,11 @@
AppWindowAnimationHelper.TransformParams liveTileParams =
v.getRecentsView().getLiveTileParams(true /* mightNeedToRefill */);
if (liveTileParams != null) {
- Collections.addAll(surfaceParamsList,
- liveTileAnimationHelper.getSurfaceParams(liveTileParams));
+ SurfaceParams[] liveTileSurfaceParams =
+ liveTileAnimationHelper.getSurfaceParams(liveTileParams);
+ if (liveTileSurfaceParams != null) {
+ Collections.addAll(surfaceParamsList, liveTileSurfaceParams);
+ }
}
}
// Apply surface transform using the surface params list.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index d644fd6..3dbe1ad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
@@ -33,6 +34,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
@@ -42,6 +44,7 @@
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.views.IconLabelDotView;
+import com.android.quickstep.views.RecentsView;
/**
* Creates an animation where all the workspace items are moved into their final location,
@@ -70,7 +73,9 @@
* @param floatingViewOriginalView The FloatingIconView's original view.
*/
public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
- float velocity) {
+ float velocity, boolean animateOverviewScrim) {
+ prepareToAnimate(launcher);
+
mVelocity = velocity;
mOriginalView = floatingViewOriginalView;
@@ -128,8 +133,10 @@
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
}
- addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
- addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+ if (animateOverviewScrim) {
+ addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
+ addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+ }
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
@@ -145,6 +152,21 @@
}
/**
+ * Setup workspace with 0 duration to prepare for our staggered animation.
+ */
+ private void prepareToAnimate(Launcher launcher) {
+ LauncherStateManager stateManager = launcher.getStateManager();
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ // setRecentsAttachedToAppWindow() will animate recents out.
+ builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
+ stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0);
+ builder.build().start();
+
+ // Stop scrolling so that it doesn't interfere with the translation offscreen.
+ launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
+ }
+
+ /**
* Starts the animation.
*/
public void start() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 56c2dd5..6e7214e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -485,6 +485,11 @@
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
+ if (!enabled) {
+ // Reset the running task when leaving overview since it can still have a reference to
+ // its thumbnail
+ mTmpRunningTask = null;
+ }
}
public void onDigitalWellbeingToastShown() {
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 0e591ca..7885f5c 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -34,7 +35,9 @@
import android.util.AttributeSet;
import android.view.animation.Interpolator;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -52,7 +55,8 @@
* From normal state to overview state, the shelf just fades in and does not move
* From overview state to all-apps state the shelf moves up and fades in to cover the screen
*/
-public class ShelfScrimView extends ScrimView implements NavigationModeChangeListener {
+public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
+ implements NavigationModeChangeListener {
// If the progress is more than this, shelf follows the finger, otherwise it moves faster to
// cover the whole screen
@@ -193,8 +197,10 @@
if (mProgress >= 1) {
mRemainingScreenColor = 0;
mShelfColor = 0;
+ LauncherState state = mLauncher.getStateManager().getState();
if (mSysUINavigationMode == Mode.NO_BUTTON
- && mLauncher.getStateManager().getState() == BACKGROUND_APP) {
+ && (state == BACKGROUND_APP || state == QUICK_SWITCH)
+ && mLauncher.getShelfPeekAnim().isPeeking()) {
// Show the shelf background when peeking during swipe up.
mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 119288c..85cffaa 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -31,9 +31,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -63,6 +60,7 @@
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@@ -132,12 +130,8 @@
@NavigationModeSwitch
@Test
+ @Ignore // b/143488140
public void goToOverviewFromHome() {
- // b/143488140
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
@@ -147,12 +141,8 @@
@NavigationModeSwitch
@Test
+ @Ignore // b/143488140
public void goToOverviewFromApp() {
- // b/143488140
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
mLauncher.getBackground().switchToOverview();
@@ -186,12 +176,8 @@
@NavigationModeSwitch
@Test
+ @Ignore // b/143488140
public void testOverview() {
- // b/143488140
- if (android.os.Build.MODEL.contains("Cuttlefish") && TestHelpers.isInLauncherProcess() &&
- (RUN_FLAFOR & (PLATFORM_PRESUBMIT | UNBUNDLED_PRESUBMIT)) != 0) {
- return;
- }
startAppFastAndWaitForRecentTask(getAppPackageName());
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 339aef5..80c791c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -130,6 +130,10 @@
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
</style>
+ <style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
<!--
Theme overrides to element on homescreen, i.e., which are drawn on top on wallpaper.
Various foreground colors are overridden to be workspaceTextColor so that they are properly
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a35f598..bc6fa6e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -403,14 +403,15 @@
Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the icons fit within the available height.
- float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
- int maxHeight = availableHeightPx - totalWorkspacePadding.y - folderMargin;
- float scaleY = maxHeight / usedHeight;
+ float contentUsedHeight = folderCellHeightPx * inv.numFolderRows;
+ int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
+ - folderMargin;
+ float scaleY = contentMaxHeight / contentUsedHeight;
// Check if the icons fit within the available width.
- float usedWidth = folderCellWidthPx * inv.numFolderColumns;
- int maxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin;
- float scaleX = maxWidth / usedWidth;
+ float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns;
+ int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin;
+ float scaleX = contentMaxWidth / contentUsedWidth;
float scale = Math.min(scaleX, scaleY);
if (scale < 1f) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 577acb2..cb8fe05 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -83,6 +83,7 @@
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
@@ -696,6 +697,7 @@
switch (requestCode) {
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
+ announceForAccessibility(R.string.item_added_to_workspace);
break;
case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(appWidgetId, info, null, null);
@@ -720,7 +722,6 @@
break;
}
}
-
return screenId;
}
@@ -1326,6 +1327,7 @@
hostView.setVisibility(View.VISIBLE);
prepareAppWidget(hostView, launcherInfo);
mWorkspace.addInScreen(hostView, launcherInfo);
+ announceForAccessibility(R.string.item_added_to_workspace);
}
private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
@@ -2415,6 +2417,10 @@
return bounceAnim;
}
+ private void announceForAccessibility(@StringRes int stringResId) {
+ getDragLayer().announceForAccessibility(getString(stringResId));
+ }
+
/**
* Add the icons for all apps.
*
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 431a149..d445bc9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1278,6 +1278,10 @@
return !mLauncher.isInState(NORMAL);
}
+ private boolean workspaceInScrollableState() {
+ return mLauncher.isInState(SPRING_LOADED) || !workspaceInModalState();
+ }
+
/** Returns whether a drag should be allowed to be started from the current workspace state. */
public boolean workspaceIconsCanBeDragged() {
return mLauncher.getStateManager().getState().workspaceIconsCanBeDragged;
@@ -2879,7 +2883,7 @@
@Override
public boolean scrollLeft() {
boolean result = false;
- if (!workspaceInModalState() && !mIsSwitchingState) {
+ if (!mIsSwitchingState && workspaceInScrollableState()) {
result = super.scrollLeft();
}
Folder openFolder = Folder.getOpen(mLauncher);
@@ -2892,7 +2896,7 @@
@Override
public boolean scrollRight() {
boolean result = false;
- if (!workspaceInModalState() && !mIsSwitchingState) {
+ if (!mIsSwitchingState && workspaceInScrollableState()) {
result = super.scrollRight();
}
Folder openFolder = Folder.getOpen(mLauncher);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0c12c60..a7ef9ef 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -191,6 +191,7 @@
ArrayList<ItemInfo> itemList = new ArrayList<>();
itemList.add(info);
mLauncher.bindItems(itemList, true);
+ announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
Workspace workspace = mLauncher.getWorkspace();
@@ -198,7 +199,6 @@
mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
- announceConfirmation(R.string.item_added_to_workspace);
}
});
return true;
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index 8ac9d66..eabd283 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -27,7 +27,7 @@
*/
public class AlphaUpdateListener extends AnimationSuccessListener
implements AnimatorUpdateListener {
- private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
+ public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
private View mView;
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 2c440bb..4a52795 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -26,15 +26,15 @@
import android.animation.ValueAnimator;
import android.util.Log;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-
/**
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
* and durations.
@@ -250,6 +250,17 @@
}
}
+ /**
+ * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This
+ * is intended to be used only if you need to cancel but want to defer cleaning up yourself.
+ */
+ public void dispatchOnCancelWithoutCancelRunnable() {
+ Runnable onCancel = mOnCancelRunnable;
+ setOnCancelRunnable(null);
+ dispatchOnCancel();
+ setOnCancelRunnable(onCancel);
+ }
+
public void dispatchOnCancel() {
dispatchOnCancelRecursively(mAnim);
}
@@ -283,10 +294,6 @@
mOnCancelRunnable = runnable;
}
- public Runnable getOnCancelRunnable() {
- return mOnCancelRunnable;
- }
-
public void skipToEnd() {
mSkipToEnd = true;
for (SpringAnimation spring : mSprings) {
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index c45cd85..fccc120 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -39,6 +39,7 @@
public static final Interpolator LINEAR = new LinearInterpolator();
public static final Interpolator ACCEL = new AccelerateInterpolator();
+ public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f);
public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
@@ -48,6 +49,7 @@
public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
+ public static final Interpolator DEACCEL_5 = new DecelerateInterpolator(5f);
public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 22dda41..0bd2c9a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -74,7 +74,10 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
@@ -145,7 +148,7 @@
public ExtendedEditText mFolderName;
private PageIndicatorDots mPageIndicator;
- private View mFooter;
+ protected View mFooter;
private int mFooterHeight;
// Cell ranks used for drag and drop
@@ -559,7 +562,11 @@
mState = STATE_OPEN;
announceAccessibilityChanges();
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
+ mLauncher.getUserEventDispatcher().logActionOnItem(
+ Touch.TAP,
+ Direction.NONE,
+ ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+
mContent.setFocusOnFirstChild();
}
});
@@ -984,10 +991,10 @@
lp.y = top;
}
- private int getContentAreaHeight() {
+ protected int getContentAreaHeight() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- int maxContentAreaHeight = grid.availableHeightPx
- - grid.getTotalWorkspacePadding().y - mFooterHeight;
+ int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y
+ - mFooterHeight;
int height = Math.min(maxContentAreaHeight,
mContent.getDesiredHeight());
return Math.max(height, MIN_CONTENT_DIMEN);
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 9eb0693..1310d37 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -59,6 +59,8 @@
*/
public class FolderAnimationManager {
+ private static final int FOLDER_NAME_ALPHA_DURATION = 32;
+
private Folder mFolder;
private FolderPagedView mContent;
private GradientDrawable mFolderBackground;
@@ -130,11 +132,19 @@
* scaleRelativeToDragLayer;
final float finalScale = 1f;
float scale = mIsOpening ? initialScale : finalScale;
- mFolder.setScaleX(scale);
- mFolder.setScaleY(scale);
mFolder.setPivotX(0);
mFolder.setPivotY(0);
+ // Scale the contents of the folder.
+ mFolder.mContent.setScaleX(scale);
+ mFolder.mContent.setScaleY(scale);
+ mFolder.mContent.setPivotX(0);
+ mFolder.mContent.setPivotY(0);
+ mFolder.mFooter.setScaleX(scale);
+ mFolder.mFooter.setScaleY(scale);
+ mFolder.mFooter.setPivotX(0);
+ mFolder.mFooter.setPivotY(0);
+
// We want to create a small X offset for the preview items, so that they follow their
// expected path to their final locations. ie. an icon should not move right, if it's final
// location is to its left. This value is arbitrarily defined.
@@ -143,14 +153,13 @@
previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX);
}
- final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
- * initialScale);
- final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
- * initialScale);
+ final int paddingOffsetX = (int) (mContent.getPaddingLeft() * initialScale);
+ final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale);
- int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX
- - previewItemOffsetX;
- int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY;
+ int initialX = folderIconPos.left + mFolder.getPaddingLeft()
+ + mPreviewBackground.getOffsetX() - paddingOffsetX - previewItemOffsetX;
+ int initialY = folderIconPos.top + mFolder.getPaddingTop()
+ + mPreviewBackground.getOffsetY() - paddingOffsetY;
final float xDistance = initialX - lp.x;
final float yDistance = initialY - lp.y;
@@ -164,11 +173,10 @@
// Set up the reveal animation that clips the Folder.
int totalOffsetX = paddingOffsetX + previewItemOffsetX;
- Rect startRect = new Rect(
- Math.round(totalOffsetX / initialScale),
- Math.round(paddingOffsetY / initialScale),
- Math.round((totalOffsetX + initialSize) / initialScale),
- Math.round((paddingOffsetY + initialSize) / initialScale));
+ Rect startRect = new Rect(totalOffsetX,
+ paddingOffsetY,
+ Math.round((totalOffsetX + initialSize)),
+ Math.round((paddingOffsetY + initialSize)));
Rect endRect = new Rect(0, 0, lp.width, lp.height);
float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics());
@@ -189,17 +197,46 @@
play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
- play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
+ play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
+ play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
play(a, getShape().createRevealAnimator(
mFolder, startRect, endRect, finalRadius, !mIsOpening));
+ // Fade in the folder name, as the text can overlap the icons when grid size is small.
+ mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
+ play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
+ mIsOpening ? FOLDER_NAME_ALPHA_DURATION : 0,
+ mIsOpening ? mDuration - FOLDER_NAME_ALPHA_DURATION : FOLDER_NAME_ALPHA_DURATION);
+
+ // Translate the footer so that it tracks the bottom of the content.
+ float normalHeight = mFolder.getContentAreaHeight();
+ float scaledHeight = normalHeight * initialScale;
+ float diff = normalHeight - scaledHeight;
+ play(a, getAnimator(mFolder.mFooter, View.TRANSLATION_Y, -diff, 0f));
// Animate the elevation midway so that the shadow is not noticeable in the background.
int midDuration = mDuration / 2;
Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
play(a, z, mIsOpening ? midDuration : 0, midDuration);
+
+ // Store clip variables
+ CellLayout cellLayout = mContent.getCurrentCellLayout();
+ boolean folderClipChildren = mFolder.getClipChildren();
+ boolean folderClipToPadding = mFolder.getClipToPadding();
+ boolean contentClipChildren = mContent.getClipChildren();
+ boolean contentClipToPadding = mContent.getClipToPadding();
+ boolean cellLayoutClipChildren = cellLayout.getClipChildren();
+ boolean cellLayoutClipPadding = cellLayout.getClipToPadding();
+
+ mFolder.setClipChildren(false);
+ mFolder.setClipToPadding(false);
+ mContent.setClipChildren(false);
+ mContent.setClipToPadding(false);
+ cellLayout.setClipChildren(false);
+ cellLayout.setClipToPadding(false);
+
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -207,8 +244,20 @@
mFolder.setTranslationX(0.0f);
mFolder.setTranslationY(0.0f);
mFolder.setTranslationZ(0.0f);
- mFolder.setScaleX(1f);
- mFolder.setScaleY(1f);
+ mFolder.mContent.setScaleX(1f);
+ mFolder.mContent.setScaleY(1f);
+ mFolder.mFooter.setScaleX(1f);
+ mFolder.mFooter.setScaleY(1f);
+ mFolder.mFooter.setTranslationX(0f);
+ mFolder.mFolderName.setAlpha(1f);
+
+ mFolder.setClipChildren(folderClipChildren);
+ mFolder.setClipToPadding(folderClipToPadding);
+ mContent.setClipChildren(contentClipChildren);
+ mContent.setClipToPadding(contentClipToPadding);
+ cellLayout.setClipChildren(cellLayoutClipChildren);
+ cellLayout.setClipToPadding(cellLayoutClipPadding);
+
}
});
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index ed08c02..598792a 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -91,7 +91,7 @@
}
public static String getTargetStr(Target t) {
- if (t == null){
+ if (t == null) {
return "";
}
String str = "";
@@ -138,17 +138,16 @@
if (t.intentHash != 0) {
typeStr += ", intentHash=" + t.intentHash;
}
- if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) &&
- t.itemType != ItemType.TASK) {
+ if (t.itemType == ItemType.FOLDER_ICON) {
+ typeStr += ", grid(" + t.gridX + "," + t.gridY + ")";
+ } else if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0)
+ && t.itemType != ItemType.TASK) {
typeStr += ", predictiveRank=" + t.predictedRank + ", grid(" + t.gridX + "," + t.gridY
- + "), span(" + t.spanX + "," + t.spanY
- + "), pageIdx=" + t.pageIndex;
-
+ + "), span(" + t.spanX + "," + t.spanY + "), pageIdx=" + t.pageIndex;
}
if (t.searchQueryLength != 0) {
typeStr += ", searchQueryLength=" + t.searchQueryLength;
}
-
if (t.itemType == ItemType.TASK) {
typeStr += ", pageIdx=" + t.pageIndex;
}
@@ -172,7 +171,7 @@
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
t.itemType = (instantAppResolver != null && info instanceof AppInfo
- && instantAppResolver.isInstantApp(((AppInfo) info)) )
+ && instantAppResolver.isInstantApp(((AppInfo) info)))
? ItemType.WEB_APP
: ItemType.APP_ICON;
t.predictedRank = DEFAULT_PREDICTED_RANK;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 7b06d3b..99906fe 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -26,6 +26,8 @@
import static com.android.launcher3.logging.LoggerUtils.newTarget;
import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
+import static java.util.Optional.ofNullable;
+
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -59,7 +61,7 @@
/**
* Manages the creation of {@link LauncherEvent}.
* To debug this class, execute following command before side loading a new apk.
- *
+ * <p>
* $ adb shell setprop log.tag.UserEvent VERBOSE
*/
public class UserEventDispatcher implements ResourceBasedOverride {
@@ -95,6 +97,7 @@
/**
* Fills in the container data on the given event if the given view is not null.
+ *
* @return whether container data was added.
*/
public boolean fillInLogContainerData(LauncherLogProto.LauncherEvent event, @Nullable View v) {
@@ -146,7 +149,11 @@
mAppOrTaskLaunch = true;
}
- public void logActionTip(int actionType, int viewType) { }
+ /**
+ * Dummy method.
+ */
+ public void logActionTip(int actionType, int viewType) {
+ }
@Deprecated
public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex,
@@ -191,15 +198,15 @@
public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
logActionCommand(command, newContainerTarget(srcContainerType),
- dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+ dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
}
public void logActionCommand(int command, int srcContainerType, int dstContainerType,
- int pageIndex) {
+ int pageIndex) {
Target srcTarget = newContainerTarget(srcContainerType);
srcTarget.pageIndex = pageIndex;
logActionCommand(command, srcTarget,
- dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+ dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
}
public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
@@ -248,7 +255,7 @@
}
public void logActionOnControl(int action, int controlType, int parentContainer,
- int grandParentContainer){
+ int grandParentContainer) {
LauncherEvent event = newLauncherEvent(newTouchAction(action),
newControlTarget(controlType),
newContainerTarget(parentContainer),
@@ -257,11 +264,11 @@
}
public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
- int parentContainerType) {
+ int parentContainerType) {
final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
: newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
- newTarget(Target.Type.CONTAINER));
+ newTarget(Target.Type.CONTAINER));
event.srcTarget[0].controlType = controlType;
if (controlInContainer != null) {
fillInLogContainerData(event, controlInContainer);
@@ -308,9 +315,9 @@
* (1) WORKSPACE: if the launcher is the foreground activity
* (2) APP: if another app was the foreground activity
*/
- public void logStateChangeAction(int action, int dir, int downX, int downY, int srcChildTargetType,
- int srcParentContainerType, int dstContainerType,
- int pageIndex) {
+ public void logStateChangeAction(int action, int dir, int downX, int downY,
+ int srcChildTargetType, int srcParentContainerType, int dstContainerType,
+ int pageIndex) {
LauncherEvent event;
if (srcChildTargetType == LauncherLogProto.ItemType.TASK) {
event = newLauncherEvent(newTouchAction(action),
@@ -333,9 +340,25 @@
}
public void logActionOnItem(int action, int dir, int itemType) {
+ logActionOnItem(action, dir, itemType, null, null);
+ }
+
+ /**
+ * Creates new {@link LauncherEvent} of ITEM target type with input arguments and dispatches it.
+ *
+ * @param touchAction ENUM value of {@link LauncherLogProto.Action.Touch} Action
+ * @param dir ENUM value of {@link LauncherLogProto.Action.Direction} Action
+ * @param itemType ENUM value of {@link LauncherLogProto.ItemType}
+ * @param gridX Nullable X coordinate of item's position on the workspace grid
+ * @param gridY Nullable Y coordinate of item's position on the workspace grid
+ */
+ public void logActionOnItem(int touchAction, int dir, int itemType,
+ @Nullable Integer gridX, @Nullable Integer gridY) {
Target itemTarget = newTarget(Target.Type.ITEM);
itemTarget.itemType = itemType;
- LauncherEvent event = newLauncherEvent(newTouchAction(action), itemTarget);
+ ofNullable(gridX).ifPresent(value -> itemTarget.gridX = value);
+ ofNullable(gridY).ifPresent(value -> itemTarget.gridY = value);
+ LauncherEvent event = newLauncherEvent(newTouchAction(touchAction), itemTarget);
event.action.dir = dir;
dispatchUserEvent(event, null);
}
@@ -358,7 +381,7 @@
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
newTarget(Target.Type.CONTAINER));
- event.destTarget = new Target[] {
+ event.destTarget = new Target[]{
newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
newDropTarget(dropTargetAsView)
};
@@ -380,14 +403,10 @@
int actionTouch = isButton ? Action.Touch.TAP : Action.Touch.SWIPE;
Action action = newCommandAction(actionTouch);
action.command = Action.Command.BACK;
- action.dir = isButton
- ? Action.Direction.NONE
- : gestureSwipeLeft
- ? Action.Direction.LEFT
- : Action.Direction.RIGHT;
- Target target = newControlTarget(isButton
- ? LauncherLogProto.ControlType.BACK_BUTTON
- : LauncherLogProto.ControlType.BACK_GESTURE);
+ action.dir = isButton ? Action.Direction.NONE :
+ gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT;
+ Target target = newControlTarget(isButton ? LauncherLogProto.ControlType.BACK_BUTTON :
+ LauncherLogProto.ControlType.BACK_GESTURE);
target.spanX = downX;
target.spanY = downY;
target.cardinality = completed ? 1 : 0;
@@ -398,6 +417,7 @@
/**
* Currently logs following containers: workspace, allapps, widget tray.
+ *
* @param reason
*/
public final void resetElapsedContainerMillis(String reason) {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 60f6ee9..f40f976 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -413,10 +413,7 @@
} else {
// Let the state manager know that the animation didn't go to the target state,
// but don't cancel ourselves (we already clean up when the animation completes).
- Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
- mCurrentAnimation.setOnCancelRunnable(null);
- mCurrentAnimation.dispatchOnCancel();
- mCurrentAnimation.setOnCancelRunnable(onCancel);
+ mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable();
endProgress = 0;
if (progress <= 0) {
diff --git a/src/com/android/launcher3/util/ObjectWrapper.java b/src/com/android/launcher3/util/ObjectWrapper.java
index b692431..e5b4707 100644
--- a/src/com/android/launcher3/util/ObjectWrapper.java
+++ b/src/com/android/launcher3/util/ObjectWrapper.java
@@ -25,7 +25,7 @@
*/
public class ObjectWrapper<T> extends Binder {
- private final T mObject;
+ private T mObject;
public ObjectWrapper(T object) {
mObject = object;
@@ -35,6 +35,10 @@
return mObject;
}
+ public void clear() {
+ mObject = null;
+ }
+
public static IBinder wrap(Object obj) {
return new ObjectWrapper<>(obj);
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 9f59d78..e64b2fb 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -77,7 +77,7 @@
/**
* Simple scrim which draws a flat color
*/
-public class ScrimView extends View implements Insettable, OnChangeListener,
+public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
AccessibilityStateChangeListener, StateListener {
public static final Property<ScrimView, Integer> DRAG_HANDLE_ALPHA =
@@ -101,7 +101,7 @@
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
- protected final Launcher mLauncher;
+ protected final T mLauncher;
private final WallpaperColorInfo mWallpaperColorInfo;
private final AccessibilityManager mAM;
protected final int mEndScrim;
@@ -130,7 +130,7 @@
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
- mLauncher = Launcher.getLauncher(context);
+ mLauncher = Launcher.cast(Launcher.getLauncher(context));
mWallpaperColorInfo = WallpaperColorInfo.getInstance(context);
mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index bb19515..4243ed0 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -123,10 +123,6 @@
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@Rule
- public ShellCommandRule mDefaultLauncherRule =
- TestHelpers.isInLauncherProcess() ? ShellCommandRule.setDefaultLauncher() : null;
-
- @Rule
public ShellCommandRule mDisableHeadsUpNotification =
ShellCommandRule.disableHeadsUpNotification();
@@ -154,9 +150,13 @@
}
protected TestRule getRulesInsideActivityMonitor() {
- return RuleChain.
- outerRule(new PortraitLandscapeRunner(this)).
- around(new FailureWatcher(mDevice));
+ final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this))
+ .around(new FailureWatcher(mDevice));
+
+ return TestHelpers.isInLauncherProcess()
+ ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
+ .around(inner) :
+ inner;
}
@Rule
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 429da21..d7096b0 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,10 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_PRESUBMIT;
-import static com.android.launcher3.util.rule.TestStabilityRule.RUN_FLAFOR;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_PRESUBMIT;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -38,7 +34,6 @@
import com.android.launcher3.tapl.AppIcon;
import com.android.launcher3.tapl.AppIconMenu;
import com.android.launcher3.tapl.AppIconMenuItem;
-import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.views.OptionsPopupView;
@@ -344,24 +339,6 @@
}
}
- /**
- * Test dragging a custom shortcut to the workspace and launch it.
- *
- * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
- * Custom shortcuts are replaced by deep shortcuts after api 25.
- */
- @Test
- @Ignore // b/143725213
- @PortraitLandscape
- public void testDragCustomShortcut() {
- if (!TestHelpers.isInLauncherProcess()) return; // b/143725213
- mLauncher.getWorkspace().openAllWidgets()
- .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
- .dragToWorkspace();
- mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
- .launch(getAppPackageName());
- }
-
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 259f9ed..f9d1d93 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -16,7 +16,6 @@
package com.android.launcher3.ui.widget;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -30,7 +29,6 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import org.junit.Rule;
import org.junit.Test;
@@ -71,4 +69,22 @@
assertNotNull("Widget not found on the workspace", widget);
widget.launch(getAppPackageName());
}
+
+ /**
+ * Test dragging a custom shortcut to the workspace and launch it.
+ *
+ * A custom shortcut is a 1x1 widget that launches a specific intent when user tap on it.
+ * Custom shortcuts are replaced by deep shortcuts after api 25.
+ */
+ @Test
+ @PortraitLandscape
+ public void testDragCustomShortcut() throws Throwable {
+ clearHomescreen();
+ mDevice.pressHome();
+ mLauncher.getWorkspace().openAllWidgets()
+ .getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
+ .dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
+ .launch(getAppPackageName());
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 01bd4e7..3bdeb14 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -110,11 +110,6 @@
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
int deviceHeight = mLauncher.getDevice().getDisplayHeight();
int displayBottom = deviceHeight - bottomGestureMargin;
- allAppsContainer.setGestureMargins(
- 0,
- getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
- 0,
- bottomGestureMargin);
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
displayBottom)) {
@@ -128,8 +123,8 @@
allAppsContainer,
mLauncher.getObjectsInContainer(allAppsContainer, "icon")
.stream()
- .filter(object ->
- object.getVisibleBounds().bottom
+ .filter(icon ->
+ icon.getVisibleBounds().bottom
<= displayBottom)
.collect(Collectors.toList()),
searchBox.getVisibleBounds().bottom
@@ -178,7 +173,8 @@
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
++attempts <= MAX_SCROLL_ATTEMPTS);
- mLauncher.scroll(allAppsContainer, Direction.UP, margins, 12);
+ mLauncher.scroll(
+ allAppsContainer, Direction.UP, margins, 12, false);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -206,7 +202,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
- allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10);
+ allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
verifyActiveContainer();
}
}
@@ -220,7 +216,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
- allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10);
+ allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 0d9038f..6583d32 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -106,7 +106,7 @@
}
if (mLauncher.isFallbackOverview()) {
- mLauncher.linearGesture(startX, startY, endX, endY, 10);
+ mLauncher.linearGesture(startX, startY, endX, endY, 10, false);
new BaseOverview(mLauncher);
} else {
mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 8ccfc05..339e14f 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -55,7 +55,8 @@
final int leftMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20);
+ mLauncher.scroll(
+ overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false);
verifyActiveContainer();
}
}
@@ -89,7 +90,8 @@
final int rightMargin = mLauncher.getTestInfo(
TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- mLauncher.scroll(overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20);
+ mLauncher.scroll(
+ overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 0d3938f..4024ff0 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -544,7 +544,8 @@
linearGesture(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
- ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
+ false);
try (LauncherInstrumentation.Closable c = addContextLayer(
"Swiped up from context menu to home")) {
waitUntilGone("deep_shortcuts_container");
@@ -696,8 +697,8 @@
final UiObject2 object = container.wait(
Until.findObject(getLauncherObjectSelector(resName)),
WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object id: " + resName + " in container: " +
- container.getResourceName(), object);
+ assertNotNull("Can't find a view in Launcher, id: " + resName + " in container: "
+ + container.getResourceName(), object);
return object;
}
@@ -706,8 +707,8 @@
final UiObject2 object = container.wait(
Until.findObject(selector),
WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
- container.getResourceName(), object);
+ assertNotNull("Can't find a view in Launcher, id: " + selector + " in container: "
+ + container.getResourceName(), object);
return object;
}
@@ -738,7 +739,7 @@
private UiObject2 waitForObjectBySelector(BySelector selector) {
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
- assertNotNull("Can't find a launcher object; selector: " + selector, object);
+ assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
return object;
}
@@ -769,7 +770,7 @@
void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
final Bundle parcel = (Bundle) executeAndWaitForEvent(
- () -> linearGesture(startX, startY, endX, endY, steps),
+ () -> linearGesture(startX, startY, endX, endY, steps, false),
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
"Swipe failed to receive an event for the swipe end");
assertEquals("Swipe switched launcher to a wrong state;",
@@ -782,34 +783,38 @@
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
}
- int getBottomGestureMargin(UiObject2 container) {
- return container.getVisibleBounds().bottom - getRealDisplaySize().y
- + getBottomGestureSize();
+ int getBottomGestureMarginInContainer(UiObject2 container) {
+ final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize();
+ return container.getVisibleBounds().bottom - bottomGestureStartOnScreen;
}
- void scrollToLastVisibleRow(UiObject2 container, Collection<UiObject2> items, int topPadding) {
+ void scrollToLastVisibleRow(
+ UiObject2 container,
+ Collection<UiObject2> items,
+ int topPaddingInContainer) {
final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top));
- final int gestureStart = lowestItem.getVisibleBounds().top + getTouchSlop();
- final int distance = gestureStart - container.getVisibleBounds().top - topPadding;
- final int bottomMargin = container.getVisibleBounds().height() - distance;
+ final int itemRowCurrentTopOnScreen = lowestItem.getVisibleBounds().top;
+ final Rect containerRect = container.getVisibleBounds();
+ final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
+ final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
- // TODO: Make the gesture steps dependent on the distance so that it can run for various
- // screen sizes
- final int totalMargin = Math.max(bottomMargin, getBottomGestureMargin(container));
+ final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
scroll(
container,
Direction.DOWN,
new Rect(
0,
- totalMargin / 2,
+ containerRect.height() - distance - bottomGestureMarginInContainer,
0,
- totalMargin / 2),
- 80);
+ bottomGestureMarginInContainer),
+ 10,
+ true);
}
- void scroll(UiObject2 container, Direction direction, Rect margins, int steps) {
+ void scroll(
+ UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) {
final Rect rect = container.getVisibleBounds();
if (margins != null) {
rect.left += margins.left;
@@ -854,7 +859,7 @@
}
executeAndWaitForEvent(
- () -> linearGesture(startX, startY, endX, endY, steps),
+ () -> linearGesture(startX, startY, endX, endY, steps, slowDown),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
"Didn't receive a scroll end message: " + startX + ", " + startY
+ ", " + endX + ", " + endY);
@@ -862,13 +867,17 @@
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
// fixed interval each time.
- void linearGesture(int startX, int startY, int endX, int endY, int steps) {
+ void linearGesture(int startX, int startY, int endX, int endY, int steps, boolean slowDown) {
log("linearGesture: " + startX + ", " + startY + " -> " + endX + ", " + endY);
final long downTime = SystemClock.uptimeMillis();
final Point start = new Point(startX, startY);
final Point end = new Point(endX, endY);
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
- final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+ long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+ if (slowDown) {
+ endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end,
+ end);
+ }
sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 91f0fc4..2ee424b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -52,7 +52,7 @@
final Rect taskBounds = mTask.getVisibleBounds();
final int centerX = taskBounds.centerX();
final int centerY = taskBounds.centerY();
- mLauncher.linearGesture(centerX, centerY, centerX, 0, 10);
+ mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false);
mLauncher.waitForIdle();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 5fcaa55..d208c66 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -48,8 +48,9 @@
mLauncher.scroll(
widgetsContainer,
Direction.DOWN,
- new Rect(0, 0, 0, mLauncher.getBottomGestureMargin(widgetsContainer) + 1),
- FLING_STEPS);
+ new Rect(0, 0, 0,
+ mLauncher.getBottomGestureMarginInContainer(widgetsContainer) + 1),
+ FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
verifyActiveContainer();
}
@@ -69,7 +70,7 @@
widgetsContainer,
Direction.UP,
new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0),
- FLING_STEPS);
+ FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index db3314e..3301dd8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -32,6 +32,7 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
/**
@@ -59,7 +60,11 @@
verifyActiveContainer();
final UiObject2 hotseat = mHotseat;
final Point start = hotseat.getVisibleCenter();
- start.y = hotseat.getVisibleBounds().bottom - 1;
+ int deviceHeight = mLauncher.getDevice().getDisplayHeight();
+ int bottomGestureMargin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
+ int displayBottom = deviceHeight - bottomGestureMargin;
+ start.y = displayBottom - 1;
final int swipeHeight = mLauncher.getTestInfo(
TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -182,7 +187,7 @@
final UiObject2 workspace = verifyActiveContainer();
mLauncher.scroll(workspace, Direction.RIGHT,
new Rect(0, 0, mLauncher.getEdgeSensitivityWidth() + 1, 0),
- FLING_STEPS);
+ FLING_STEPS, false);
verifyActiveContainer();
}
@@ -194,7 +199,7 @@
final UiObject2 workspace = verifyActiveContainer();
mLauncher.scroll(workspace, Direction.LEFT,
new Rect(mLauncher.getEdgeSensitivityWidth() + 1, 0, 0, 0),
- FLING_STEPS);
+ FLING_STEPS, false);
verifyActiveContainer();
}