From e9c6f4cc6965943c09b06765e380f695fccece51 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 30 May 2019 11:35:25 -0700 Subject: [PATCH 01/18] Fixing interactive state for background states and notifying LauncherCallback accordingly During quick switch Launcher can get resumed only to be paused again when launching a new app. Updating the logic for deferred resume to account for that and moving some additional calls to deferredResume Bug: 134062513 Change-Id: Ia607f178bc17d45d53fb032a06cd70f9abc6b513 --- .../states/BackgroundAppState.java | 3 +- src/com/android/launcher3/Launcher.java | 38 +++++++++++-------- .../android/launcher3/LauncherCallbacks.java | 6 ++- 3 files changed, 30 insertions(+), 17 deletions(-) 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/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 711cfd2885..e6f583f47d 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; @@ -890,26 +891,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 @@ -923,14 +938,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/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. */ From 55da87181cae4ed53884910ab5fca8b1917d7fe6 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 30 May 2019 14:45:18 -0700 Subject: [PATCH 02/18] Waiting for the recycler to populate when opening all apps Change-Id: Ia6d452f9b02f02c1414eaaa9eaee9a70a4664a4a --- .../com/android/launcher3/tapl/AllApps.java | 8 ++++++- .../tapl/LauncherInstrumentation.java | 21 ++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) 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..443bc1af14 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; @@ -570,6 +570,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 +590,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 +605,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); } From 38d8a63d07317a0bf4ec6807005472a8461747fd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 May 2019 14:59:19 -0700 Subject: [PATCH 03/18] Disabling horizontal swipes if the app has draggable content at the bottom Bug: 133844237 Change-Id: I67a4140d56f87c464e436d394da77127a965c3e5 --- .../quickstep/TouchInteractionService.java | 33 +++++++++++++++++-- .../OtherActivityInputConsumer.java | 26 +++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) 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/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) { From 1689d65ac3286fa7830b7689eb93c3b8a801d252 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 30 May 2019 15:54:07 -0700 Subject: [PATCH 04/18] If y velocity > x velocity in 2-button mode, go to recents Also, when landing in recents, snap to the nearest page so that swipe up consitently goes to task 0 instead of sometimes scrolling to task 1. Bug: 133518678 Change-Id: I63a161a9f1f87266d3c3fc71c98cd5fc5e2f8874 --- .../WindowTransformSwipeHandler.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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..51e5e22a3b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -106,7 +106,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; @@ -902,10 +901,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 +979,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); } From b3c9077c7676015253ee9334ac08f7c3d87f41b4 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 30 May 2019 17:35:44 -0700 Subject: [PATCH 05/18] Tuning app open/close animation. * Mostly value changes. * Added a way to round the corners during swipe up to home animation. Bug: 123900446 Change-Id: Id61d241d919ba51ced0633585e36b7d93efe30b0 --- .../res/values/dimens.xml | 2 +- .../WindowTransformSwipeHandler.java | 14 +++++++++++-- .../quickstep/util/ClipAnimationHelper.java | 20 +++++++++++++++---- .../util/StaggeredWorkspaceAnim.java | 13 ++++++------ .../QuickstepAppTransitionManagerImpl.java | 7 +++++-- .../launcher3/anim/FlingSpringAnim.java | 2 +- .../launcher3/views/FloatingIconView.java | 6 +++--- 7 files changed, 44 insertions(+), 20 deletions(-) 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/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 0d0478ae05..9f446c8ab4 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; @@ -1131,16 +1132,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 */); 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 e2fb602d92..aca23e4e40 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/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/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 7a6da3eec5..e4591b6183 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") { From 2ff7b53ec5769decc59b24d3e3035fef1327999d Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 30 May 2019 20:53:27 -0700 Subject: [PATCH 06/18] Fix bug where QSB flickers on app close. Bug: 123900446 Change-Id: I9b03b2d91d264de237328511b5406e5fcd22dbac --- .../src/com/android/quickstep/WindowTransformSwipeHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6a3983e285..cc4a1e05cc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -1067,8 +1067,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 { From 33838e8a2f5183f281910ac639f2ef43d23d0042 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 31 May 2019 08:54:35 -0700 Subject: [PATCH 07/18] Letting touch pass even in disabled states In disabled states, dragLayer should not receive touch anyway. Removing the additional check to avoid user getting stuck. Bug: 134062513 Change-Id: Id31a31254377e9a737ecd6e9905e66c54e4eb740 --- src/com/android/launcher3/dragndrop/DragLayer.java | 10 ---------- 1 file changed, 10 deletions(-) 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); } From 254be7b0e4367fbd4d88ebde338938150773a965 Mon Sep 17 00:00:00 2001 From: Govinda Wasserman Date: Fri, 31 May 2019 12:20:50 -0400 Subject: [PATCH 08/18] Adds trigger for new gesture entrypoint to Assistant bundle BUG:130186141 Change-Id: I43a4c3d3c9a46b4a1a5c53a81b037ba2be194188 FIX:130186141 --- .../quickstep/inputconsumers/AssistantTouchConsumer.java | 5 +++++ 1 file changed, 5 insertions(+) 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; From 436d5971b1b84e491f8e15ee9bbd8c2253e5f637 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 31 May 2019 10:21:28 -0700 Subject: [PATCH 09/18] Don't recreate the launcher animation controller if gesture ended This can happen if we get onApplyWindowInsets() after onGestureEnded(). In that case, we can get into some bad states because the running animation is overwritten and thus can't be canceled. Bug: 134096917 Bug: 133806285 Change-Id: Ic517d68a46a4446ca382445df1ba62c26e8243ad --- .../com/android/quickstep/WindowTransformSwipeHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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..030d0dd5c8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -106,7 +106,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; @@ -647,6 +646,9 @@ public class WindowTransformSwipeHandler } private void buildAnimationController() { + if (mStateCallback.hasStates(STATE_GESTURE_COMPLETED)) { + return; + } initTransitionEndpoints(mActivity.getDeviceProfile()); mAnimationFactory.createActivityController(mTransitionDragLength); } From 03d32ddb2358f1cbd3886dd41b69b4cc5ce21d75 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 30 May 2019 14:23:30 -0700 Subject: [PATCH 10/18] Fixing overview scrim not initialized properly when launcher starts State UI depends on visible content, which can change due to predicitons. Reapplying the state whenever then happens ensures that we are in correct start UI Change-Id: I9f195a92b747fda8a5b217dc960f230d7a695255 --- .../appprediction/PredictionRowView.java | 22 +++++-------------- .../PredictionUiStateManager.java | 8 +------ 2 files changed, 7 insertions(+), 23 deletions(-) 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); } } From 8fcf7059edc7d7ff9156d63e29a2b9a08236d311 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 31 May 2019 11:32:09 -0700 Subject: [PATCH 11/18] Annotating hierarchy dump for the error description Change-Id: I10285d451f4816b4988ce0645669b9ec9cceac81 --- .../com/android/launcher3/tapl/LauncherInstrumentation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index f7befd1408..ee12b278cf 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); } From a8f77a6afd75f8bd18778d09a9eb6363b5f9bcc7 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 31 May 2019 13:27:24 -0700 Subject: [PATCH 12/18] More debug tracing for switching to all apps not sending the final event (to the test) Bug: 134172603 Bug: 133867119 Change-Id: I00cf3a35daefa23d2e72a89d21e6ce37c8a9a46d --- .../android/launcher3/LauncherStateManager.java | 6 ++++++ .../allapps/AllAppsTransitionController.java | 6 ++++++ .../AbstractStateChangeTouchController.java | 16 ++++++++++++++++ .../android/launcher3/touch/SwipeDetector.java | 12 ++++++++++++ 4 files changed, 40 insertions(+) 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/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/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) { From 464dda250b107f147f825972c0d0012d8b4b700c Mon Sep 17 00:00:00 2001 From: Perumaal S Date: Fri, 31 May 2019 13:55:27 -0700 Subject: [PATCH 13/18] Disable chips in qt_dev - Not for use in qt_r1_dev Bug: 134164918 Change-Id: Ib49289060a39a1a5615e762a4e72b105ed166135 --- src/com/android/launcher3/config/BaseFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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( From a04e746e84f918fd5e6d4fa2cda7b60d90462cf0 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 31 May 2019 13:33:00 -0700 Subject: [PATCH 14/18] Fix getting stuck in BackgroundAppState for 2-button mode When ending in recents, we reapply the state - therefore, it's important to make sure we are in OverviewState before onSwipeUpComplete(). This is done by mLauncherTransitionController, which sets OverviewState on end. We already force mLauncherTransitionController to end before calling onSwipeUpComplete(), but in this case we were calling cancel() and setting mLauncherTransitionController = null, which meant we could never call end() on it. Instead, we should always call end() if we set it to null. Also ensure mLauncherTransitionController is created even if the gesture is completed, if an existing controller isn't already running. This can happen if you swipe up quickly enough that we get onGestureEnd before launcher is drawn, and in that case we still want the launcher component to animate once its ready. This is even more important for 2-button mode, because again, we rely on mLauncherTransitionController to set the state to OverviewState before we reapply it. Finally, clarified some methods by renaming "swipeUp" to "swipeUpToRecents". Bug: 132757019 Change-Id: Ieb24a4f36a39780e5d64d7bc312791608db474d1 --- .../quickstep/GoActivityControlHelper.java | 2 +- .../FallbackActivityControllerHelper.java | 2 +- .../LauncherActivityControllerHelper.java | 2 +- .../WindowTransformSwipeHandler.java | 30 ++++++++++--------- .../quickstep/ActivityControlHelper.java | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) 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/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/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index c7aaa9bb27..87b7326648 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -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,7 +647,10 @@ public class WindowTransformSwipeHandler } private void buildAnimationController() { - if (mStateCallback.hasStates(STATE_GESTURE_COMPLETED)) { + 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()); @@ -1276,12 +1279,7 @@ public class WindowTransformSwipeHandler } private void invalidateHandlerWithLauncher() { - if (mLauncherTransitionController != null) { - if (mLauncherTransitionController.getAnimationPlayer().isStarted()) { - mLauncherTransitionController.getAnimationPlayer().cancel(); - } - mLauncherTransitionController = null; - } + endLauncherTransitionController(); mRecentsView.onGestureAnimationEnd(); @@ -1289,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(); } @@ -1390,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/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); From acdde3d750611476264a838d0a536d99404a692b Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Sat, 1 Jun 2019 15:04:52 -0700 Subject: [PATCH 15/18] Fix issue where Workspace scrolling doesn't work => The WorkspaceTouchListener was relying on receiving TOUCH_DOWN in order to clear its state. This is not guaranteed; ie. when touches begin above children of the workspace (icons or widgets) => Any invocation of the Workspace long press menu would trigger this issue and it would persist until a touch went directly onto the Workspace in a location without any children. Bug 132298752 Change-Id: Id8617baaa1ce59dc84758a7c82049329323b04cc --- src/com/android/launcher3/touch/WorkspaceTouchListener.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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; } From f69426c4845f35206ad370889cb489d2f4af0135 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Sat, 25 May 2019 22:15:48 -0700 Subject: [PATCH 16/18] Add shape detection logic only for circle icons b/119330044 Change-Id: I93042effa8417167d844f073275e8b941e861e60 --- .../launcher3/icons/BaseIconFactory.java | 21 ++-- .../launcher3/icons/IconNormalizer.java | 105 +++++++++++++++++- .../android/launcher3/dragndrop/DragView.java | 2 +- .../android/launcher3/graphics/IconShape.java | 9 ++ .../launcher3/icons/LauncherIcons.java | 15 ++- .../launcher3/views/FloatingIconView.java | 3 +- 6 files changed, 139 insertions(+), 16 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index 3c5585421a..db5515b741 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; } @@ -212,18 +218,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/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/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 03cbb21437..63916ed8fc 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -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( From d17a951df84abf3b66f9c16d0f736d150c99b9d8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 31 May 2019 16:02:23 -0700 Subject: [PATCH 17/18] Removing user locked change in LauncherAppState The process will crash anyway when loading sharedPreference. So we do not need any extra check to cause the same crash. Bug: 134094839 Change-Id: Icfd4406ff601d6b9a75bd95ddcecb9869f7e7fa2 --- src/com/android/launcher3/LauncherAppState.java | 3 --- 1 file changed, 3 deletions(-) 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"); From 3ca5280186aad28a47916ecbf52cead127bb40b7 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 3 Jun 2019 18:35:52 -0700 Subject: [PATCH 18/18] Fixing All Apps's getting stuck in a non-updating state Normally, if all apps is open, and an app installs/uninstalls/updates, All Apps will immediately reflect this. However, depending on something subtle in the intensity of the swipe gesture that brought All Apps, all apps will freeze, and won't update. This frozen state will go away after scrolling in all apps, iteration with an icon and, generally, any tap interaction with All Apps. Otherwise, it will stay until it's closed and opened again via a gesture with a different pattern. The reason is in the code that freezes All Apps updates during user interactions with all apps. For example, during scrolls. Or while the user holds an icon, expecting to see a context menu; in this case an update would cause the menu to not appear, which is somewhat annoying. The motivation to add this code was to solve a category of lab-only flakes when a context menu couldn't open because the lab device was busy with post-flash activities. The code incorrectly assumed that after ACTION_DOWN, we are guaranteed to get either UP or CANCEL, which is wrong. It's after *consumed* ACTION_DOWN that we'll get these events. The fix still solves the user's and tests' problems that the code was supposed to solve. Bug: 134442147 Change-Id: I9db74a33ecf93b1dc6bc69df301f7f542dea2a40 --- src/com/android/launcher3/allapps/AllAppsContainerView.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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; } }