diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java index 8b6f8bcfe0..2db8b39d94 100644 --- a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java +++ b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java @@ -29,7 +29,7 @@ public abstract class GoActivityControlHelper im } @Override - public void onSwipeUpComplete(T activity) { + public void onSwipeUpToRecentsComplete(T activity) { // Go does not support swipe up gesture. } diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index 7cd99efffa..60320d63be 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -46,13 +46,15 @@ public class BaseIconFactory implements AutoCloseable { private IconNormalizer mNormalizer; private ShadowGenerator mShadowGenerator; + private final boolean mShapeDetection; private Drawable mWrapperIcon; private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; - protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) { + protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, + boolean shapeDetection) { mContext = context.getApplicationContext(); - + mShapeDetection = shapeDetection; mFillResIconDpi = fillResIconDpi; mIconBitmapSize = iconBitmapSize; @@ -64,6 +66,10 @@ public class BaseIconFactory implements AutoCloseable { clear(); } + protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) { + this(context, fillResIconDpi, iconBitmapSize, false); + } + protected void clear() { mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; mDisableColorExtractor = false; @@ -78,7 +84,7 @@ public class BaseIconFactory implements AutoCloseable { public IconNormalizer getNormalizer() { if (mNormalizer == null) { - mNormalizer = new IconNormalizer(mIconBitmapSize); + mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection); } return mNormalizer; } @@ -209,18 +215,19 @@ public class BaseIconFactory implements AutoCloseable { } AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon; dr.setBounds(0, 0, 1, 1); - scale = getNormalizer().getScale(icon, outIconBounds); - if (!(icon instanceof AdaptiveIconDrawable)) { + boolean[] outShape = new boolean[1]; + scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape); + if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) { FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground()); fsd.setDrawable(icon); fsd.setScale(scale); icon = dr; - scale = getNormalizer().getScale(icon, outIconBounds); + scale = getNormalizer().getScale(icon, outIconBounds, null, null); ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor); } } else { - scale = getNormalizer().getScale(icon, outIconBounds); + scale = getNormalizer().getScale(icon, outIconBounds, null, null); } outScale[0] = scale; diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java index 4a2a7cf9ff..de39e79fec 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java @@ -18,16 +18,22 @@ package com.android.launcher3.icons; import android.annotation.TargetApi; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.os.Build; +import android.util.Log; import java.nio.ByteBuffer; @@ -51,6 +57,9 @@ public class IconNormalizer { private static final int MIN_VISIBLE_ALPHA = 40; + // Shape detection related constants + private static final float BOUND_RATIO_MARGIN = .05f; + private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; private static final float SCALE_NOT_INITIALIZED = 0; // Ratio of the diameter of an normalized circular icon to the actual icon size. @@ -59,18 +68,24 @@ public class IconNormalizer { private final int mMaxSize; private final Bitmap mBitmap; private final Canvas mCanvas; + private final Paint mPaintMaskShape; + private final Paint mPaintMaskShapeOutline; private final byte[] mPixels; private final RectF mAdaptiveIconBounds; private float mAdaptiveIconScale; + private boolean mEnableShapeDetection; + // for each y, stores the position of the leftmost x and the rightmost x private final float[] mLeftBorder; private final float[] mRightBorder; private final Rect mBounds; + private final Path mShapePath; + private final Matrix mMatrix; /** package private **/ - IconNormalizer(int iconBitmapSize) { + IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) { // Use twice the icon size as maximum size to avoid scaling down twice. mMaxSize = iconBitmapSize * 2; mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); @@ -81,7 +96,22 @@ public class IconNormalizer { mBounds = new Rect(); mAdaptiveIconBounds = new RectF(); + mPaintMaskShape = new Paint(); + mPaintMaskShape.setColor(Color.RED); + mPaintMaskShape.setStyle(Paint.Style.FILL); + mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); + + mPaintMaskShapeOutline = new Paint(); + mPaintMaskShapeOutline.setStrokeWidth( + 2 * context.getResources().getDisplayMetrics().density); + mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); + mPaintMaskShapeOutline.setColor(Color.BLACK); + mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + mShapePath = new Path(); + mMatrix = new Matrix(); mAdaptiveIconScale = SCALE_NOT_INITIALIZED; + mEnableShapeDetection = shapeDetection; } private static float getScale(float hullArea, float boundingArea, float fullArea) { @@ -126,6 +156,72 @@ public class IconNormalizer { return getScale(hullArea, hullArea, size * size); } + /** + * Returns if the shape of the icon is same as the path. + * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds. + */ + private boolean isShape(Path maskPath) { + // Condition1: + // If width and height of the path not close to a square, then the icon shape is + // not same as the mask shape. + float iconRatio = ((float) mBounds.width()) / mBounds.height(); + if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) { + if (DEBUG) { + Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio); + } + return false; + } + + // Condition 2: + // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation + // should generate transparent image, if the actual icon is equivalent to the shape. + + // Fit the shape within the icon's bounding box + mMatrix.reset(); + mMatrix.setScale(mBounds.width(), mBounds.height()); + mMatrix.postTranslate(mBounds.left, mBounds.top); + maskPath.transform(mMatrix, mShapePath); + + // XOR operation + mCanvas.drawPath(mShapePath, mPaintMaskShape); + + // DST_OUT operation around the mask path outline + mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline); + + // Check if the result is almost transparent + return isTransparentBitmap(); + } + + /** + * Used to determine if certain the bitmap is transparent. + */ + private boolean isTransparentBitmap() { + ByteBuffer buffer = ByteBuffer.wrap(mPixels); + buffer.rewind(); + mBitmap.copyPixelsToBuffer(buffer); + + int y = mBounds.top; + // buffer position + int index = y * mMaxSize; + // buffer shift after every row, width of buffer = mMaxSize + int rowSizeDiff = mMaxSize - mBounds.right; + + int sum = 0; + for (; y < mBounds.bottom; y++) { + index += mBounds.left; + for (int x = mBounds.left; x < mBounds.right; x++) { + if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { + sum++; + } + index++; + } + index += rowSizeDiff; + } + + float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); + return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; + } + /** * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it * matches the design guidelines for a launcher icon. @@ -140,7 +236,8 @@ public class IconNormalizer { * * @param outBounds optional rect to receive the fraction distance from each edge. */ - public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) { + public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, + @Nullable Path path, @Nullable boolean[] outMaskShape) { if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) { if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds); @@ -242,7 +339,9 @@ public class IconNormalizer { 1 - ((float) mBounds.right) / width, 1 - ((float) mBounds.bottom) / height); } - + if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) { + outMaskShape[0] = isShape(path); + } // Area of the rectangle required to fit the convex hull float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); return getScale(area, rectArea, width * height); diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml index c80e531e63..863a8ba528 100644 --- a/quickstep/recents_ui_overrides/res/values/dimens.xml +++ b/quickstep/recents_ui_overrides/res/values/dimens.xml @@ -27,5 +27,5 @@ 18dp 10dp - -80dp + -60dp \ No newline at end of file diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java index 4a486f8e5c..cb5cbddd4a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -164,13 +164,6 @@ public class PredictionRowView extends LinearLayout implements mParent = parent; } - private void setPredictionsEnabled(boolean predictionsEnabled) { - if (predictionsEnabled != mPredictionsEnabled) { - mPredictionsEnabled = predictionsEnabled; - updateVisibility(); - } - } - private void updateVisibility() { setVisibility(mPredictionsEnabled ? VISIBLE : GONE); } @@ -220,8 +213,7 @@ public class PredictionRowView extends LinearLayout implements * If the number of predicted apps is the same as the previous list of predicted apps, * we can optimize by swapping them in place. */ - public void setPredictedApps(boolean predictionsEnabled, List apps) { - setPredictionsEnabled(predictionsEnabled); + public void setPredictedApps(List apps) { mPredictedAppComponents.clear(); mPredictedAppComponents.addAll(apps); @@ -237,11 +229,6 @@ public class PredictionRowView extends LinearLayout implements } private void applyPredictionApps() { - if (!mPredictionsEnabled) { - mParent.onHeightUpdated(); - return; - } - if (getChildCount() != mNumPredictedAppsPerRow) { while (getChildCount() > mNumPredictedAppsPerRow) { removeViewAt(0); @@ -282,8 +269,11 @@ public class PredictionRowView extends LinearLayout implements } } - if (predictionCount == 0) { - setPredictionsEnabled(false); + boolean predictionsEnabled = predictionCount > 0; + if (predictionsEnabled != mPredictionsEnabled) { + mPredictionsEnabled = predictionsEnabled; + mLauncher.reapplyUi(); + updateVisibility(); } mParent.onHeightUpdated(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java index 64cb4b465f..085bbc4a5e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java @@ -177,16 +177,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf } private void applyState(PredictionState state) { - boolean wasEnabled = mCurrentState.isEnabled; mCurrentState = state; if (mAppsView != null) { mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class) - .setPredictedApps(mCurrentState.isEnabled, mCurrentState.apps); - - if (wasEnabled != mCurrentState.isEnabled) { - // Reapply state as the State UI might have changed. - Launcher.getLauncher(mAppsView.getContext()).getStateManager().reapplyState(true); - } + .setPredictedApps(mCurrentState.apps); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index a662d7433e..8436fe5f9d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -32,7 +32,8 @@ import com.android.quickstep.views.TaskView; public class BackgroundAppState extends OverviewState { private static final int STATE_FLAGS = - FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY; + FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY + | FLAG_DISABLE_INTERACTION; public BackgroundAppState(int id) { this(id, LauncherLogProto.ContainerType.TASKSWITCHER); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java index f12efc83cd..dc58a4efc9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java @@ -70,7 +70,7 @@ public final class FallbackActivityControllerHelper implements } @Override - public void onSwipeUpComplete(RecentsActivity activity) { + public void onSwipeUpToRecentsComplete(RecentsActivity activity) { RecentsView recentsView = activity.getOverviewPanel(); recentsView.getClearAllButton().setVisibilityAlpha(1); recentsView.setDisallowScrollToClearAll(false); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 5af09f7fd9..d0a41f3b53 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -91,7 +91,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe } @Override - public void onSwipeUpComplete(Launcher activity) { + public void onSwipeUpToRecentsComplete(Launcher activity) { // Re apply state in case we did something funky during the transition. activity.getStateManager().reapplyState(); DiscoveryBounce.showForOverviewIfNeeded(activity); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 7563c3f55d..294a997f3d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -58,6 +58,8 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.WindowManager; +import androidx.annotation.BinderThread; + import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; @@ -84,6 +86,7 @@ import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; +import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -268,6 +271,9 @@ public class TouchInteractionService extends Service implements private final RectF mSwipeTouchRegion = new RectF(); private ComponentName mGestureBlockingActivity; + private Region mExclusionRegion; + private SystemGestureExclusionListenerCompat mExclusionListener; + @Override public void onCreate() { super.onCreate(); @@ -284,14 +290,23 @@ public class TouchInteractionService extends Service implements mIsUserUnlocked = false; registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); } - onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this)); mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay() .getDisplayId(); - String blockingActivity = getString(R.string.gesture_blocking_activity); mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null : ComponentName.unflattenFromString(blockingActivity); + + mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) { + @Override + @BinderThread + public void onExclusionChanged(Region region) { + // Assignments are atomic, it should be safe on binder thread + mExclusionRegion = region; + } + }; + + onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this)); sConnected = true; } @@ -370,6 +385,12 @@ public class TouchInteractionService extends Service implements disposeEventHandlers(); initInputMonitor(); + + if (mMode == Mode.NO_BUTTON) { + mExclusionListener.register(); + } else { + mExclusionListener.unregister(); + } } @Override @@ -437,6 +458,7 @@ public class TouchInteractionService extends Service implements sConnected = false; Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver); SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); + mExclusionListener.unregister(); super.onDestroy(); } @@ -557,10 +579,15 @@ public class TouchInteractionService extends Service implements final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event); + + // mExclusionRegion can change on binder thread, use a local instance here. + Region exclusionRegion = mExclusionRegion; + boolean disableHorizontalSwipe = mMode == Mode.NO_BUTTON && exclusionRegion != null + && exclusionRegion.contains((int) event.getX(), (int) event.getY()); return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel, mOverviewComponentObserver.getOverviewIntent(), activityControl, shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive, - mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion); + mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 0d0478ae05..87b7326648 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -19,6 +19,7 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; import static com.android.launcher3.Utilities.postAsyncCallback; +import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -106,7 +107,6 @@ import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.LatencyTrackerCompat; -import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.WindowCallbacksCompat; @@ -353,7 +353,7 @@ public class WindowTransformSwipeHandler | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED | STATE_GESTURE_STARTED, - this::setupLauncherUiAfterSwipeUpAnimation); + this::setupLauncherUiAfterSwipeUpToRecentsAnimation); mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, @@ -647,6 +647,12 @@ public class WindowTransformSwipeHandler } private void buildAnimationController() { + if (mGestureEndTarget == HOME || (mLauncherTransitionController != null + && mLauncherTransitionController.getAnimationPlayer().isStarted())) { + // We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it + // has its own animation) or if we're already animating the current controller. + return; + } initTransitionEndpoints(mActivity.getDeviceProfile()); mAnimationFactory.createActivityController(mTransitionDragLength); } @@ -902,10 +908,14 @@ public class WindowTransformSwipeHandler // If swiping at a diagonal, base end target on the faster velocity. endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity) ? NEW_TASK : HOME; - } else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) { - // If user scrolled to a new task, only go to recents if they already passed - // the overview threshold. Otherwise, we'll snap to the new task and launch it. - endTarget = RECENTS; + } else if (endVelocity < 0) { + if (reachedOverviewThreshold) { + endTarget = RECENTS; + } else { + // If swiping at a diagonal, base end target on the faster velocity. + endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity) + ? NEW_TASK : RECENTS; + } } else { endTarget = goingToNewTask ? NEW_TASK : LAST_TASK; } @@ -976,6 +986,12 @@ public class WindowTransformSwipeHandler } else if (endTarget == RECENTS) { mLiveTileOverlay.startIconAnimation(); if (mRecentsView != null) { + int nearestPage = mRecentsView.getPageNearestToCenterOfScreen(); + if (mRecentsView.getNextPage() != nearestPage) { + // We shouldn't really scroll to the next page when swiping up to recents. + // Only allow settling on the next page if it's nearest to the center. + mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration)); + } if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) { mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION); } @@ -1057,8 +1073,8 @@ public class WindowTransformSwipeHandler setStateOnUiThread(target.endState); } }); - homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); windowAnim.start(velocityPxPerMs); + homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); mLauncherTransitionController = null; } else { @@ -1131,16 +1147,25 @@ public class WindowTransformSwipeHandler AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome(); + // End on a "round-enough" radius so that the shape reveal doesn't have to do too much + // rounding at the end of the animation. + float startRadius = mClipAnimationHelper.getCurrentCornerRadius(); + float endRadius = startRect.width() / 6f; // We want the window alpha to be 0 once this threshold is met, so that the // FolderIconView can be seen morphing into the icon shape. final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f; anim.addOnUpdateListener((currentRect, progress) -> { homeAnim.setPlayFraction(progress); - float windowAlpha = Math.max(0, Utilities.mapToRange(progress, 0, - windowAlphaThreshold, 1f, 0f, Interpolators.LINEAR)); + float alphaProgress = ACCEL_1_5.getInterpolation(progress); + float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0, + windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1); mTransformParams.setProgress(progress) .setCurrentRectAndTargetAlpha(currentRect, windowAlpha); + if (isFloatingIconView) { + mTransformParams.setCornerRadius(endRadius * progress + startRadius + * (1f - progress)); + } mClipAnimationHelper.applyTransform(targetSet, mTransformParams, false /* launcherOnTop */); @@ -1254,12 +1279,7 @@ public class WindowTransformSwipeHandler } private void invalidateHandlerWithLauncher() { - if (mLauncherTransitionController != null) { - if (mLauncherTransitionController.getAnimationPlayer().isStarted()) { - mLauncherTransitionController.getAnimationPlayer().cancel(); - } - mLauncherTransitionController = null; - } + endLauncherTransitionController(); mRecentsView.onGestureAnimationEnd(); @@ -1267,6 +1287,13 @@ public class WindowTransformSwipeHandler mActivity.getRootView().getOverlay().remove(mLiveTileOverlay); } + private void endLauncherTransitionController() { + if (mLauncherTransitionController != null) { + mLauncherTransitionController.getAnimationPlayer().end(); + mLauncherTransitionController = null; + } + } + private void notifyTransitionCancelled() { mAnimationFactory.onTransitionCancelled(); } @@ -1368,12 +1395,9 @@ public class WindowTransformSwipeHandler doLogGesture(HOME); } - private void setupLauncherUiAfterSwipeUpAnimation() { - if (mLauncherTransitionController != null) { - mLauncherTransitionController.getAnimationPlayer().end(); - mLauncherTransitionController = null; - } - mActivityControlHelper.onSwipeUpComplete(mActivity); + private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { + endLauncherTransitionController(); + mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity); mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true); mRecentsView.onSwipeUpAnimationSuccess(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index bf276e1e16..837423aceb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -62,6 +62,10 @@ public class AssistantTouchConsumer extends DelegateInputConsumer private static final String TAG = "AssistantTouchConsumer"; private static final long RETRACT_ANIMATION_DURATION_MS = 300; + // From //java/com/google/android/apps/gsa/search/shared/util/OpaContract.java. + private static final String OPA_BUNDLE_TRIGGER = "triggered_by"; + // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto. + private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83; private static final String INVOCATION_TYPE_KEY = "invocation_type"; private static final int INVOCATION_TYPE_GESTURE = 1; private static final int INVOCATION_TYPE_FLING = 6; @@ -230,6 +234,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer startAssistantInternal(SWIPE); Bundle args = new Bundle(); + args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE); args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); mSysUiProxy.startAssistant(args); mLaunchedAssistant = true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index b0acffa398..0ed4c99a6f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -111,6 +111,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final float mDragSlop; private final float mSquaredTouchSlop; + private final boolean mDisableHorizontalSwipe; // Slop used to check when we start moving window. private boolean mPassedDragSlop; @@ -132,7 +133,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC InputConsumerController inputConsumer, Consumer onCompleteCallback, SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat, - RectF swipeTouchRegion) { + RectF swipeTouchRegion, boolean disableHorizontalSwipe) { super(base); mMainThreadHandler = new Handler(Looper.getMainLooper()); @@ -162,6 +163,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mSquaredTouchSlop = slop * slop; mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture; + mDisableHorizontalSwipe = !mPassedTouchSlop && disableHorizontalSwipe; } @Override @@ -169,6 +171,13 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC return TYPE_OTHER_ACTIVITY; } + private void forceCancelGesture(MotionEvent ev) { + int action = ev.getAction(); + ev.setAction(ACTION_CANCEL); + finishTouchTracking(ev); + ev.setAction(action); + } + @Override public void onMotionEvent(MotionEvent ev) { if (mVelocityTracker == null) { @@ -216,10 +225,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Cancel interaction in case of multi-touch interaction int ptrIdx = ev.getActionIndex(); if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) { - int action = ev.getAction(); - ev.setAction(ACTION_CANCEL); - finishTouchTracking(ev); - ev.setAction(action); + forceCancelGesture(ev); } } break; @@ -258,7 +264,15 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } if (!mPassedTouchSlop) { - if (squaredHypot(displacementX, mLastPos.y - mDownPos.y) >= mSquaredTouchSlop) { + float displacementY = mLastPos.y - mDownPos.y; + if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) { + if (mDisableHorizontalSwipe + && Math.abs(displacementX) > Math.abs(displacementY)) { + // Horizontal gesture is not allowed in this region + forceCancelGesture(ev); + break; + } + mPassedTouchSlop = true; if (mIsDeferredDownTarget) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java index 0ae469c703..19a4963467 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -197,10 +197,15 @@ public class ClipAnimationHelper { mTmpMatrix.postTranslate(app.position.x, app.position.y); mClipRectF.roundOut(crop); if (mSupportsRoundedCornersOnWindows) { - float windowCornerRadius = mUseRoundedCornersOnWindows - ? mWindowCornerRadius : 0; - cornerRadius = Utilities.mapRange(progress, windowCornerRadius, - mTaskCornerRadius); + if (params.cornerRadius > -1) { + cornerRadius = params.cornerRadius; + scale = params.currentRect.width() / crop.width(); + } else { + float windowCornerRadius = mUseRoundedCornersOnWindows + ? mWindowCornerRadius : 0; + cornerRadius = Utilities.mapRange(progress, windowCornerRadius, + mTaskCornerRadius); + } mCurrentCornerRadius = cornerRadius; } } @@ -355,6 +360,7 @@ public class ClipAnimationHelper { @Nullable RectF currentRect; float targetAlpha; boolean forLiveTile; + float cornerRadius; SyncRtSurfaceTransactionApplierCompat syncTransactionApplier; @@ -365,6 +371,7 @@ public class ClipAnimationHelper { currentRect = null; targetAlpha = 0; forLiveTile = false; + cornerRadius = -1; } public TransformParams setProgress(float progress) { @@ -373,6 +380,11 @@ public class ClipAnimationHelper { return this; } + public TransformParams setCornerRadius(float cornerRadius) { + this.cornerRadius = cornerRadius; + return this; + } + public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) { this.currentRect = currentRect; this.targetAlpha = targetAlpha; 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 93b6e4ba5d..837c2dc935 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 @@ -44,14 +44,13 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; */ public class StaggeredWorkspaceAnim { - private static final int APP_CLOSE_ROW_START_DELAY_MS = 16; - private static final int ALPHA_DURATION_MS = 200; + private static final int APP_CLOSE_ROW_START_DELAY_MS = 10; + private static final int ALPHA_DURATION_MS = 250; private static final float MAX_VELOCITY_PX_PER_S = 22f; - private static final float DAMPING_RATIO = - (SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY + SpringForce.DAMPING_RATIO_LOW_BOUNCY) / 2f; - private static final float STIFFNESS = SpringForce.STIFFNESS_LOW; + private static final float DAMPING_RATIO = 0.7f; + private static final float STIFFNESS = 150f; private final float mVelocity; private final float mSpringTransY; @@ -71,9 +70,9 @@ public class StaggeredWorkspaceAnim { // Scale the translationY based on the initial velocity to better sync the workspace items // with the floating view. - float transFactor = 0.1f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S; + float transFactor = 0.2f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S; mSpringTransY = transFactor * launcher.getResources() - .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);; + .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y); DeviceProfile grid = launcher.getDeviceProfile(); ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace() diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 79540c1645..dcf2e3c155 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -119,6 +119,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private static final long APP_LAUNCH_ALPHA_DOWN_DURATION = (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); + private static final long CROP_DURATION = 375; + private static final long RADIUS_DURATION = 375; + public static final int RECENTS_LAUNCH_DURATION = 336; private static final int LAUNCHER_RESUME_START_DELAY = 100; private static final int CLOSING_TRANSITION_DURATION_MS = 250; @@ -494,10 +497,10 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans EXAGGERATED_EASE); FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY, alphaDuration, LINEAR); - FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, APP_LAUNCH_DURATION, + FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION, EXAGGERATED_EASE); FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0, - APP_LAUNCH_DURATION, EXAGGERATED_EASE); + RADIUS_DURATION, EXAGGERATED_EASE); @Override public void onUpdate(float percent) { diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 279a946195..b17d8d69c7 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -51,7 +51,7 @@ public interface ActivityControlHelper { int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect); - void onSwipeUpComplete(T activity); + void onSwipeUpToRecentsComplete(T activity); void onAssistantVisibilityChanged(float visibility); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ed0b90fcce..855535b0f3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -277,6 +277,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, final Handler mHandler = new Handler(); private final Runnable mHandleDeferredResume = this::handleDeferredResume; + private boolean mDeferredResumePending; private float mCurrentAssistantVisibility = 0f; @@ -889,26 +890,40 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } private void handleDeferredResume() { - if (hasBeenResumed()) { + if (hasBeenResumed() && !mStateManager.getState().disableInteraction) { getUserEventDispatcher().logActionCommand(Action.Command.RESUME, mStateManager.getState().containerType, -1); getUserEventDispatcher().startSession(); UiFactory.onLauncherStateOrResumeChanged(this); AppLaunchTracker.INSTANCE.get(this).onReturnedToHome(); - resetPendingActivityResultIfNeeded(); - } - } - private void resetPendingActivityResultIfNeeded() { - if (hasBeenResumed() && mPendingActivityRequestCode != -1 && isInState(NORMAL)) { - UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode); + // Process any items that were added while Launcher was away. + InstallShortcutReceiver.disableAndFlushInstallQueue( + InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); + + // Refresh shortcuts if the permission changed. + mModel.refreshShortcutsIfRequired(); + + DiscoveryBounce.showForHomeIfNeeded(this); + + if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) { + UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode); + } + mDeferredResumePending = false; + } else { + mDeferredResumePending = true; } } protected void onStateSet(LauncherState state) { getAppWidgetHost().setResumed(state == LauncherState.NORMAL); - resetPendingActivityResultIfNeeded(); + if (mDeferredResumePending) { + handleDeferredResume(); + } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onStateChanged(); + } } @Override @@ -922,14 +937,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, Utilities.postAsyncCallback(mHandler, mHandleDeferredResume); setOnResumeCallback(null); - // Process any items that were added while Launcher was away. - InstallShortcutReceiver.disableAndFlushInstallQueue( - InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); - // Refresh shortcuts if the permission changed. - mModel.refreshShortcutsIfRequired(); - - DiscoveryBounce.showForHomeIfNeeded(this); if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index dc275161d4..d07638a5c1 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -68,9 +68,6 @@ public class LauncherAppState { } private LauncherAppState(Context context) { - if (!UserManagerCompat.getInstance(context).isUserUnlocked(Process.myUserHandle())) { - throw new RuntimeException("LauncherAppState should not start in direct boot mode"); - } if (getLocalProvider(context) == null) { throw new RuntimeException( "Initializing LauncherAppState in the absence of LauncherProvider"); diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index edac516e14..dfe75ec34a 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -21,7 +21,6 @@ import android.os.Bundle; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; /** * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks @@ -53,6 +52,11 @@ public interface LauncherCallbacks { boolean handleBackPressed(); void onTrimMemory(int level); + /** + * Called when the launcher state changed + */ + default void onStateChanged() { } + /* * Extension points for providing custom behavior on certain user interactions. */ diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 3edd8385ab..fe6b522970 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -40,6 +40,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.os.Handler; import android.os.Looper; +import android.util.Log; import androidx.annotation.IntDef; @@ -474,6 +475,11 @@ public class LauncherStateManager { // Only change the stable states after the transitions have finished if (state != mCurrentStableState) { mLastStableState = state.getHistoryForState(mCurrentStableState); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, + "mCurrentStableState = " + state.getClass().getSimpleName() + " @ " + + android.util.Log.getStackTraceString(new Throwable())); + } mCurrentStableState = state; } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 053c570045..0d43e214ef 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -625,15 +625,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo @Override public boolean dispatchTouchEvent(MotionEvent ev) { + final boolean result = super.dispatchTouchEvent(ev); switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: - mAllAppsStore.setDeferUpdates(true); + if (result) mAllAppsStore.setDeferUpdates(true); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mAllAppsStore.setDeferUpdates(false); break; } - return super.dispatchTouchEvent(ev); + return result; } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index a7f89d9c07..c62fc3d8f9 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -14,6 +14,7 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.util.Log; import android.util.Property; import android.view.animation.Interpolator; @@ -29,6 +30,7 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; @@ -162,6 +164,10 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil @Override public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder, AnimationConfig config) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, + "setStateWithAnimation " + toState.getClass().getSimpleName()); + } float targetProgress = toState.getVerticalProgress(mLauncher); if (Float.compare(mProgress, targetProgress) == 0) { setAlphas(toState, config, builder); diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java index f53ea51509..eaf3b1cb24 100644 --- a/src/com/android/launcher3/anim/FlingSpringAnim.java +++ b/src/com/android/launcher3/anim/FlingSpringAnim.java @@ -29,7 +29,7 @@ public class FlingSpringAnim { private static final float FLING_FRICTION = 1.5f; private static final float SPRING_STIFFNESS = 200; - private static final float SPRING_DAMPING = 0.85f; + private static final float SPRING_DAMPING = 0.8f; private final FlingAnimation mFlingAnim; private SpringAnimation mSpringAnim; diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 54d0db1006..7e20d11c2b 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -105,7 +105,7 @@ abstract class BaseFlags { "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview"); public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag( - "ENABLE_HINTS_IN_OVERVIEW", true, + "ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen"); public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag( diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index b35e23ce96..6ba015b2e4 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -130,16 +130,6 @@ public class DragLayer extends BaseDragLayer { return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); } - @Override - protected boolean findActiveController(MotionEvent ev) { - if (mActivity.getStateManager().getState().disableInteraction) { - // You Shall Not Pass!!! - mActiveController = null; - return true; - } - return super.findActiveController(ev); - } - private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) { return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev); } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index d1bd2db134..09c5e5b2b2 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -243,7 +243,7 @@ public class DragView extends View implements LauncherStateManager.StateListener nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null); } Utilities.scaleRectAboutCenter(bounds, - li.getNormalizer().getScale(nDr, null)); + li.getNormalizer().getScale(nDr, null, null, null)); } AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr; diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java index 88e4452e17..4369385ae1 100644 --- a/src/com/android/launcher3/graphics/IconShape.java +++ b/src/com/android/launcher3/graphics/IconShape.java @@ -91,6 +91,10 @@ public abstract class IconShape { private SparseArray mAttrs; + public boolean enableShapeDetection(){ + return false; + }; + public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint paint); @@ -194,6 +198,11 @@ public abstract class IconShape { protected float getStartRadius(Rect startRect) { return startRect.width() / 2f; } + + @Override + public boolean enableShapeDetection() { + return true; + } } public static class RoundedSquare extends SimpleRectShape { diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index 5c4af1fe6f..3b74307dbb 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -30,6 +30,7 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.util.Themes; @@ -50,11 +51,15 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { private static LauncherIcons sPool; private static int sPoolId = 0; + public static LauncherIcons obtain(Context context) { + return obtain(context, IconShape.getShape().enableShapeDetection()); + } + /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ - public static LauncherIcons obtain(Context context) { + public static LauncherIcons obtain(Context context, boolean shapeDetection) { int poolId; synchronized (sPoolSync) { if (sPool != null) { @@ -67,7 +72,8 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { } InvariantDeviceProfile idp = LauncherAppState.getIDP(context); - return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId); + return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId, + shapeDetection); } public static void clearPool() { @@ -81,8 +87,9 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { private LauncherIcons next; - private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { - super(context, fillResIconDpi, iconBitmapSize); + private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId, + boolean shapeDetection) { + super(context, fillResIconDpi, iconBitmapSize, shapeDetection); mPoolId = poolId; } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 9703aa6267..4e5f7a59a9 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -31,6 +31,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.os.SystemClock; +import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; @@ -118,6 +119,9 @@ public abstract class AbstractStateChangeTouchController @Override public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 1 " + ev); + } if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !canInterceptTouch(ev); if (mNoIntercept) { @@ -147,6 +151,9 @@ public abstract class AbstractStateChangeTouchController return false; } + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 2 "); + } onControllerTouchEvent(ev); return mDetector.isDraggingOrSettling(); } @@ -234,6 +241,9 @@ public abstract class AbstractStateChangeTouchController @Override public void onDragStart(boolean start) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 1 " + start); + } mStartState = mLauncher.getStateManager().getState(); if (mStartState == ALL_APPS) { mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS; @@ -243,6 +253,9 @@ public abstract class AbstractStateChangeTouchController mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER; } if (mCurrentAnimation == null) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 2"); + } mFromState = mStartState; mToState = null; cancelAnimationControllers(); @@ -552,6 +565,9 @@ public abstract class AbstractStateChangeTouchController } private void cancelAnimationControllers() { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "cancelAnimationControllers"); + } mCurrentAnimation = null; cancelAtomicComponentsController(); mDetector.finishedScrolling(); diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java index 4616e58fec..3d454046a5 100644 --- a/src/com/android/launcher3/touch/SwipeDetector.java +++ b/src/com/android/launcher3/touch/SwipeDetector.java @@ -158,6 +158,9 @@ public class SwipeDetector { // SETTLING -> (View settled) -> IDLE private void setState(ScrollState newState) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- start: " + newState); + } if (DBG) { Log.d(TAG, "setState:" + mState + "->" + newState); } @@ -165,6 +168,9 @@ public class SwipeDetector { if (newState == ScrollState.DRAGGING) { initializeDragging(); if (mState == ScrollState.IDLE) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- 1: " + newState); + } reportDragStart(false /* recatch */); } else if (mState == ScrollState.SETTLING) { reportDragStart(true /* recatch */); @@ -318,9 +324,15 @@ public class SwipeDetector { break; } mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 1"); + } // handle state and listener calls. if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 2"); + } setState(ScrollState.DRAGGING); } if (mState == ScrollState.DRAGGING) { diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index 07ddccb313..310d598b25 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -99,7 +99,6 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY()); } - cancelLongPress(); if (handleLongPress) { mLongPressState = STATE_REQUESTED; mTouchDownPoint.set(ev.getX(), ev.getY()); @@ -148,6 +147,10 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe } } + if (action == ACTION_UP || action == ACTION_CANCEL) { + cancelLongPress(); + } + return result; } diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 7a6da3eec5..95c96817a0 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -77,7 +77,7 @@ import androidx.dynamicanimation.animation.SpringForce; public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener { - public static final float SHAPE_PROGRESS_DURATION = 0.15f; + public static final float SHAPE_PROGRESS_DURATION = 0.10f; private static final int FADE_DURATION_MS = 200; private static final Rect sTmpRect = new Rect(); private static final RectF sTmpRectF = new RectF(); @@ -85,8 +85,8 @@ public class FloatingIconView extends View implements // We spring the foreground drawable relative to the icon's movement in the DragLayer. // We then use these two factor values to scale the movement of the fg within this view. - private static final int FG_TRANS_X_FACTOR = 80; - private static final int FG_TRANS_Y_FACTOR = 100; + private static final int FG_TRANS_X_FACTOR = 60; + private static final int FG_TRANS_Y_FACTOR = 75; private static final FloatPropertyCompat mFgTransYProperty = new FloatPropertyCompat("FloatingViewFgTransY") { @@ -529,7 +529,8 @@ public class FloatingIconView extends View implements bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2); try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) { - Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null)); + Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null, + null, null)); } bounds.inset( diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 18a8f2755a..d03035a871 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -20,8 +20,10 @@ import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel import android.graphics.Point; import android.graphics.Rect; +import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; @@ -42,6 +44,10 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { super(launcher); final UiObject2 allAppsContainer = verifyActiveContainer(); mHeight = allAppsContainer.getVisibleBounds().height(); + final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer, + "apps_list_view"); + // Wait for the recycler to populate. + mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class)); } @Override @@ -115,7 +121,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { verifyActiveContainer(); } - final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, + final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler, appIconSelector); ensureIconVisible(appIcon, allAppsContainer, appListRecycler); return new AppIcon(mLauncher, appIcon); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index f7befd1408..e45fca8c0f 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -21,9 +21,9 @@ import static android.content.pm.PackageManager.DONT_KILL_APP; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; +import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; -import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import android.app.ActivityManager; import android.app.Instrumentation; @@ -257,6 +257,7 @@ public final class LauncherInstrumentation { } private void fail(String message) { + log("Hierarchy dump for: " + getContextDescription() + message); dumpViewHierarchy(); Assert.fail("http://go/tapl : " + getContextDescription() + message); } @@ -570,6 +571,16 @@ public final class LauncherInstrumentation { return object; } + @NonNull + UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) { + 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); + return object; + } + @Nullable private boolean hasLauncherObject(String resId) { return mDevice.hasObject(getLauncherObjectSelector(resId)); @@ -580,11 +591,6 @@ public final class LauncherInstrumentation { return waitForObjectBySelector(getLauncherObjectSelector(resName)); } - @NonNull - UiObject2 waitForLauncherObjectByClass(String clazz) { - return waitForObjectBySelector(getLauncherObjectSelectorByClass(clazz)); - } - @NonNull UiObject2 waitForFallbackLauncherObject(String resName) { return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName)); @@ -600,10 +606,6 @@ public final class LauncherInstrumentation { return By.res(getLauncherPackageName(), resName); } - BySelector getLauncherObjectSelectorByClass(String clazz) { - return By.pkg(getLauncherPackageName()).clazz(clazz); - } - BySelector getFallbackLauncherObjectSelector(String resName) { return By.res(getOverviewPackageName(), resName); }