From 142dcd8489054cd5673b4ddf1546a94fe82e922f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 1 May 2020 16:21:10 -0700 Subject: [PATCH 01/19] Fixing exception when applying transaction while the surface is released > Check surface before applying transaction > Keeping the release check around even if it release true the first time, as some other check may defer release > Waiting on animation complete before releasing a surface Bug: 148885018 Bug: 148194313 Change-Id: Ieb2b98492ff7df165e6b28a108b5f2cbc7ded32d --- .../AppToOverviewAnimationProvider.java | 5 +- .../android/quickstep/BaseSwipeUpHandler.java | 18 ++- .../com/android/quickstep/TaskViewUtils.java | 7 +- .../quickstep/util/RectFSpringAnim.java | 9 +- .../util/SurfaceTransactionApplier.java | 136 ++++++++++++++++++ .../quickstep/util/TransformParams.java | 4 +- .../android/quickstep/views/RecentsView.java | 6 +- .../QuickstepAppTransitionManagerImpl.java | 14 +- .../quickstep/RemoteAnimationTargets.java | 73 ++++++++-- .../config/robolectric.properties | 2 +- ...a => ShadowSurfaceTransactionApplier.java} | 6 +- 11 files changed, 236 insertions(+), 44 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java rename robolectric_tests/src/com/android/launcher3/shadows/{ShadowSyncRtSurfaceTransactionApplierCompat.java => ShadowSurfaceTransactionApplier.java} (83%) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index c18a0fdebb..e5782e7a45 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -33,11 +33,11 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.quickstep.util.RemoteAnimationProvider; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; /** * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview. @@ -132,8 +132,7 @@ final class AppToOverviewAnimationProvider> extend TransformParams params = new TransformParams() .setTargetSet(targets) - .setSyncTransactionApplier( - new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView())); + .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView())); AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { }); params.setBaseBuilderProxy((builder, app, p) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 737d837ac7..aa34bad8a0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -40,13 +40,14 @@ import com.android.launcher3.util.WindowBounds; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.RecentsView; 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.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import java.util.ArrayList; import java.util.function.Consumer; @@ -114,10 +115,10 @@ public abstract class BaseSwipeUpHandler, Q extend public abstract Intent getLaunchIntent(); protected void linkRecentsViewScroll() { - SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> { + SurfaceTransactionApplier.create(mRecentsView, applier -> { mTransformParams.setSyncTransactionApplier(applier); runOnRecentsAnimationStart(() -> - mRecentsAnimationTargets.addDependentTransactionApplier(applier)); + mRecentsAnimationTargets.addReleaseCheck(applier)); }); mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { @@ -364,6 +365,17 @@ public abstract class BaseSwipeUpHandler, Q extend } } + @Override + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + RectFSpringAnim anim = + super.createWindowAnimationToHome(startProgress, homeAnimationFactory); + if (mRecentsAnimationTargets != null) { + mRecentsAnimationTargets.addReleaseCheck(anim); + } + return anim; + } + public interface Factory { BaseSwipeUpHandler newHandler( diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index c68d6e2e0d..e2e25f32d3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -42,6 +42,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.util.DefaultDisplay; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.RecentsView; @@ -49,7 +50,6 @@ import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; /** * Utility class for helpful methods related to {@link TaskView} objects and their tasks. @@ -128,11 +128,10 @@ public final class TaskViewUtils { RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController, PendingAnimation out) { - SyncRtSurfaceTransactionApplierCompat applier = - new SyncRtSurfaceTransactionApplierCompat(v); + SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); final RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); - targets.addDependentTransactionApplier(applier); + targets.addReleaseCheck(applier); TransformParams params = new TransformParams() .setSyncTransactionApplier(applier) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index 8a6c4a17ae..e5d2c53d60 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -29,6 +29,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.FlingSpringAnim; import com.android.launcher3.util.DynamicResource; +import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck; import com.android.systemui.plugins.ResourceProvider; import java.util.ArrayList; @@ -39,7 +40,7 @@ import java.util.List; * Applies spring forces to animate from a starting rect to a target rect, * while providing update callbacks to the caller. */ -public class RectFSpringAnim { +public class RectFSpringAnim extends ReleaseCheck { private static final FloatPropertyCompat RECT_CENTER_X = new FloatPropertyCompat("rectCenterXSpring") { @@ -116,6 +117,7 @@ public class RectFSpringAnim { ResourceProvider rp = DynamicResource.provider(context); mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change); mYOvershoot = rp.getDimension(R.dimen.swipe_up_y_overshoot); + setCanRelease(true); } public void onTargetPositionChanged() { @@ -190,10 +192,12 @@ public class RectFSpringAnim { maybeOnEnd(); }); + setCanRelease(false); + mAnimsStarted = true; + mRectXAnim.start(); mRectYAnim.start(); mRectScaleAnim.start(); - mAnimsStarted = true; for (Animator.AnimatorListener animatorListener : mAnimatorListeners) { animatorListener.onAnimationStart(null); } @@ -245,6 +249,7 @@ public class RectFSpringAnim { private void maybeOnEnd() { if (mAnimsStarted && isEnded()) { mAnimsStarted = false; + setCanRelease(true); for (Animator.AnimatorListener animatorListener : mAnimatorListeners) { animatorListener.onAnimationEnd(null); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java new file mode 100644 index 0000000000..0a3e3ecf11 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.systemui.shared.system.TransactionCompat.deferTransactionUntil; +import static com.android.systemui.shared.system.TransactionCompat.setEarlyWakeup; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.View; + +import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; +import com.android.systemui.shared.system.ViewRootImplCompat; + +import java.util.function.Consumer; + + +/** + * Helper class to apply surface transactions in sync with RenderThread similar to + * android.view.SyncRtSurfaceTransactionApplier + * with some Launcher specific utility methods + */ +@TargetApi(Build.VERSION_CODES.R) +public class SurfaceTransactionApplier extends ReleaseCheck { + + private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0; + + private final SurfaceControl mBarrierSurfaceControl; + private final ViewRootImplCompat mTargetViewRootImpl; + private final Handler mApplyHandler; + + private int mLastSequenceNumber = 0; + + /** + * @param targetView The view in the surface that acts as synchronization anchor. + */ + public SurfaceTransactionApplier(View targetView) { + mTargetViewRootImpl = new ViewRootImplCompat(targetView); + mBarrierSurfaceControl = mTargetViewRootImpl.getRenderSurfaceControl(); + mApplyHandler = new Handler(this::onApplyMessage); + } + + protected boolean onApplyMessage(Message msg) { + if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) { + setCanRelease(msg.arg1 == mLastSequenceNumber); + return true; + } + return false; + } + + /** + * Schedules applying surface parameters on the next frame. + * + * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into + * this method to avoid synchronization issues. + */ + public void scheduleApply(final SurfaceParams... params) { + View view = mTargetViewRootImpl.getView(); + if (view == null) { + return; + } + + mLastSequenceNumber++; + final int toApplySeqNo = mLastSequenceNumber; + setCanRelease(false); + mTargetViewRootImpl.registerRtFrameCallback(frame -> { + if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) { + Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) + .sendToTarget(); + return; + } + Transaction t = new Transaction(); + for (int i = params.length - 1; i >= 0; i--) { + SurfaceParams surfaceParams = params[i]; + if (surfaceParams.surface.isValid()) { + deferTransactionUntil(t, surfaceParams.surface, mBarrierSurfaceControl, frame); + surfaceParams.applyTo(t); + } + } + setEarlyWakeup(t); + t.apply(); + Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) + .sendToTarget(); + }); + + // Make sure a frame gets scheduled. + view.invalidate(); + } + + /** + * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is + * attached if necessary. + */ + public static void create( + final View targetView, final Consumer callback) { + if (targetView == null) { + // No target view, no applier + callback.accept(null); + } else if (new ViewRootImplCompat(targetView).isValid()) { + // Already attached, we're good to go + callback.accept(new SurfaceTransactionApplier(targetView)); + } else { + // Haven't been attached before we can get the view root + targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + targetView.removeOnAttachStateChangeListener(this); + callback.accept(new SurfaceTransactionApplier(targetView)); + } + + @Override + public void onViewDetachedFromWindow(View v) { + // Do nothing + } + }); + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java index 9bb508eb37..7a62e83c39 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java @@ -57,7 +57,7 @@ public class TransformParams { private float mTargetAlpha; private float mCornerRadius; private RemoteAnimationTargets mTargetSet; - private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier; + private SurfaceTransactionApplier mSyncTransactionApplier; private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; @@ -112,7 +112,7 @@ public class TransformParams { * are computed based on these TransformParams. */ public TransformParams setSyncTransactionApplier( - SyncRtSurfaceTransactionApplierCompat applier) { + SurfaceTransactionApplier applier) { mSyncTransactionApplier = applier; return this; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a506b7eb0c..27492587e0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -127,6 +127,7 @@ import com.android.quickstep.ViewUtils; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitScreenBounds; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; @@ -135,7 +136,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.LauncherEventUtil; import com.android.systemui.shared.system.PackageManagerWrapper; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; @@ -211,7 +211,7 @@ public abstract class RecentsView extends PagedView impl protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; - protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier; + protected SurfaceTransactionApplier mSyncTransactionApplier; protected int mTaskWidth; protected int mTaskHeight; protected boolean mEnableDrawingLiveTile = false; @@ -501,7 +501,7 @@ public abstract class RecentsView extends PagedView impl mModel.getThumbnailCache().getHighResLoadingState().addCallback(this); mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); + mSyncTransactionApplier = new SurfaceTransactionApplier(this); RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); mIPinnedStackAnimationListener.setActivity(mActivity); diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 25c07f15b8..fe7b946ef4 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -82,6 +82,7 @@ import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.systemui.shared.system.ActivityCompat; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.QuickStepContract; @@ -89,7 +90,6 @@ import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -455,9 +455,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(floatingView); - openingTargets.addDependentTransactionApplier(surfaceApplier); + SurfaceTransactionApplier surfaceApplier = + new SurfaceTransactionApplier(floatingView); + openingTargets.addReleaseCheck(surfaceApplier); // Scale the app icon to take up the entire screen. This simplifies the math when // animating the app window position / scale. @@ -712,8 +712,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans */ private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets) { - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mDragLayer); + SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer); ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1); unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS); float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 : @@ -741,8 +740,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans */ private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets) { - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mDragLayer); + SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer); Matrix matrix = new Matrix(); Point tmpPos = new Point(); ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1); diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index f90df4563f..ab5e3ba0cc 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -16,19 +16,16 @@ package com.android.quickstep; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Queue; +import java.util.concurrent.CopyOnWriteArrayList; /** * Holds a collection of RemoteAnimationTargets, filtered by different properties. */ public class RemoteAnimationTargets { - private final Queue mDependentTransactionAppliers = - new ArrayDeque<>(1); + private final CopyOnWriteArrayList mReleaseChecks = new CopyOnWriteArrayList<>(); public final RemoteAnimationTargetCompat[] unfilteredApps; public final RemoteAnimationTargetCompat[] apps; @@ -36,6 +33,8 @@ public class RemoteAnimationTargets { public final int targetMode; public final boolean hasRecents; + private boolean mReleased = false; + public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers, int targetMode) { ArrayList filteredApps = new ArrayList<>(); @@ -76,21 +75,65 @@ public class RemoteAnimationTargets { return false; } - public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) { - mDependentTransactionAppliers.add(delay); + public void addReleaseCheck(ReleaseCheck check) { + mReleaseChecks.add(check); } public void release() { - SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll(); - if (applier == null) { - for (RemoteAnimationTargetCompat target : unfilteredApps) { - target.release(); + if (mReleased) { + return; + } + for (ReleaseCheck check : mReleaseChecks) { + if (!check.mCanRelease) { + check.addOnSafeToReleaseCallback(this::release); + return; } - for (RemoteAnimationTargetCompat target : wallpapers) { - target.release(); + } + mReleaseChecks.clear(); + mReleased = true; + + for (RemoteAnimationTargetCompat target : unfilteredApps) { + target.release(); + } + for (RemoteAnimationTargetCompat target : wallpapers) { + target.release(); + } + } + + /** + * Interface for intercepting surface release method + */ + public static class ReleaseCheck { + + boolean mCanRelease = false; + private Runnable mAfterApplyCallback; + + protected void setCanRelease(boolean canRelease) { + mCanRelease = canRelease; + if (mCanRelease && mAfterApplyCallback != null) { + Runnable r = mAfterApplyCallback; + mAfterApplyCallback = null; + r.run(); + } + } + + /** + * Adds a callback to notify when the surface can safely be released + */ + void addOnSafeToReleaseCallback(Runnable callback) { + if (mCanRelease) { + callback.run(); + } else { + if (mAfterApplyCallback == null) { + mAfterApplyCallback = callback; + } else { + final Runnable oldCallback = mAfterApplyCallback; + mAfterApplyCallback = () -> { + callback.run(); + oldCallback.run(); + }; + } } - } else { - applier.addAfterApplyCallback(this::release); } } } diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties index c162255bb4..b1717120af 100644 --- a/robolectric_tests/config/robolectric.properties +++ b/robolectric_tests/config/robolectric.properties @@ -13,6 +13,6 @@ shadows= \ com.android.launcher3.shadows.ShadowLooperExecutor \ com.android.launcher3.shadows.ShadowMainThreadInitializedObject \ com.android.launcher3.shadows.ShadowOverrides \ - com.android.launcher3.shadows.ShadowSyncRtSurfaceTransactionApplierCompat \ + com.android.launcher3.shadows.ShadowSurfaceTransactionApplier \ application=com.android.launcher3.util.LauncherTestApplication \ No newline at end of file diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java similarity index 83% rename from robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java rename to robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java index 238926c65f..a9f2f270a3 100644 --- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowSyncRtSurfaceTransactionApplierCompat.java +++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowSurfaceTransactionApplier.java @@ -26,11 +26,11 @@ import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; /** - * Shadow for SyncRtSurfaceTransactionApplierCompat to override default functionality + * Shadow for SurfaceTransactionApplier to override default functionality */ -@Implements(className = "com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat", +@Implements(className = "com.android.quickstep.util.SurfaceTransactionApplier", isInAndroidSdk = false) -public class ShadowSyncRtSurfaceTransactionApplierCompat { +public class ShadowSurfaceTransactionApplier { @RealObject private Object mRealObject; From d811fe19a7212849dc6b26e2e5eb15b3a1821338 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Fri, 29 May 2020 16:07:05 -0700 Subject: [PATCH 02/19] Make "Don't suggest app" default True Bug: 157011277 Change-Id: I84ab3502ae18d28e3108e9d10a24a08f55dcaff3 --- src/com/android/launcher3/config/FeatureFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 978dd52a4f..118ce0c44d 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -103,7 +103,7 @@ public final class FeatureFlags { "Adds localized title and keyword search and ranking"); public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag( - "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list"); + "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list"); public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag( "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); From 10c2b4f9d9b398bde933c1e10c10480819eb8e56 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 1 Jun 2020 14:20:30 -0500 Subject: [PATCH 03/19] Update home to overview depth interpolator to match other motion Overview comes in and workspace goes out at overshoot(1.2), so the wallpaper depth/blur should match that speed. Test: go to overview from home in 3 button or 0 button mode, ensure wallpaper scales down at the same rate as other elements Bug: 154637581 Change-Id: I03254fa3fdf19f468852bed8aab7ba21203c429a --- .../uioverrides/states/QuickstepAtomicAnimationFactory.java | 2 ++ .../android/launcher3/statehandlers/DepthController.java | 4 +++- src/com/android/launcher3/states/StateAnimationConfig.java | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 94c7771513..bf4c05da99 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -36,6 +36,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; @@ -209,6 +210,7 @@ public class QuickstepAtomicAnimationFactory extends } config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); + config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2); Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mActivity) ? OVERSHOOT_1_2 diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 8292a92af6..df1b18e018 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -17,6 +17,7 @@ package com.android.launcher3.statehandlers; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER; import android.os.IBinder; @@ -172,11 +173,12 @@ public class DepthController implements StateHandler { float toDepth = toState.getDepth(mLauncher); if (Float.compare(mDepth, toDepth) != 0) { - animation.setFloat(this, DEPTH, toDepth, LINEAR); + animation.setFloat(this, DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR)); } } private void setDepth(float depth) { + depth = Utilities.boundToRange(depth, 0, 1); // Round out the depth to dedupe frequent, non-perceptable updates int depthI = (int) (depth * 256); float depthF = depthI / 256f; diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java index 1c4986728f..f90ad3cd0b 100644 --- a/src/com/android/launcher3/states/StateAnimationConfig.java +++ b/src/com/android/launcher3/states/StateAnimationConfig.java @@ -69,7 +69,8 @@ public class StateAnimationConfig { ANIM_ALL_APPS_FADE, ANIM_OVERVIEW_SCRIM_FADE, ANIM_ALL_APPS_HEADER_FADE, - ANIM_OVERVIEW_MODAL + ANIM_OVERVIEW_MODAL, + ANIM_DEPTH, }) @Retention(RetentionPolicy.SOURCE) public @interface AnimType {} @@ -87,8 +88,9 @@ public class StateAnimationConfig { public static final int ANIM_OVERVIEW_SCRIM_FADE = 11; public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions public static final int ANIM_OVERVIEW_MODAL = 13; + public static final int ANIM_DEPTH = 14; - private static final int ANIM_TYPES_COUNT = 14; + private static final int ANIM_TYPES_COUNT = 15; private final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT]; From bd7ee7366d33dd346b4b2e954fe92b3b88b16fa7 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 29 May 2020 13:38:44 -0700 Subject: [PATCH 04/19] Checking for success of interactions with Wellbeing Bug: 157589968 Change-Id: I9a5cc6341c0133ae5e31c47b6bd89f80e15c9099 --- .../src/com/android/launcher3/model/WellbeingModel.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index 2181aa826a..f42b124fd6 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -77,6 +77,7 @@ public final class WellbeingModel { private static final String EXTRA_ACTION = "action"; private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown"; private static final String EXTRA_PACKAGES = "packages"; + private static final String EXTRA_SUCCESS = "success"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(WellbeingModel::new); @@ -221,6 +222,7 @@ public final class WellbeingModel { params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1); // Perform wellbeing call . remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params); + if (!remoteActionBundle.getBoolean(EXTRA_SUCCESS, true)) return false; synchronized (mModelLock) { // Remove the entries for requested packages, and then update the fist with what we @@ -281,9 +283,9 @@ public final class WellbeingModel { // Remove all existing messages mWorkerHandler.removeCallbacksAndMessages(null); final String[] packageNames = mContext.getSystemService(LauncherApps.class) - .getActivityList(null, Process.myUserHandle()).stream() - .map(li -> li.getApplicationInfo().packageName).distinct() - .toArray(String[]::new); + .getActivityList(null, Process.myUserHandle()).stream() + .map(li -> li.getApplicationInfo().packageName).distinct() + .toArray(String[]::new); if (!updateActions(packageNames)) { scheduleRefreshRetry(msg); } From 034b9b67c8dae5fe1281e58155b974ae01b3f0a3 Mon Sep 17 00:00:00 2001 From: sallyyuen Date: Mon, 1 Jun 2020 20:02:06 -0700 Subject: [PATCH 05/19] Use global action constant for a11y All Apps action "All apps" will be identified by a11y services with new id. Bug: 157078174 Test: Manual with TalkBack Change-Id: I6b47b93e0141a7d2a28a21ef35f80157b413a55e --- .../src/com/android/quickstep/TouchInteractionService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 22e689f7ba..6da7dad826 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -134,10 +134,10 @@ public class TouchInteractionService extends Service implements PluginListener Date: Tue, 2 Jun 2020 13:02:03 -0700 Subject: [PATCH 06/19] Fixing nullpointerexception when applying transformParams Bug: 158015382 Change-Id: Idf979f9d1786e9cb3ee0870f43ee47a735bb595a --- .../src/com/android/quickstep/BaseSwipeUpHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 737d837ac7..045f459ce0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -356,7 +356,8 @@ public abstract class BaseSwipeUpHandler, Q extend if (mWindowTransitionController != null) { float progress = mCurrentShift.value / mDragLengthFactor; mWindowTransitionController.setPlayFraction(progress); - + } + if (mRecentsAnimationTargets != null) { if (mRecentsViewScrollLinked) { mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset()); } From 568ac00832727a34afd1e82477da99ffe3624a83 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 2 Jun 2020 13:20:10 -0700 Subject: [PATCH 07/19] Logging for swiping to home not working Bug: 158017601 Change-Id: Ibe92a7f07a4eb880b7b54b486236ca90e74353f7 --- .../android/quickstep/TouchInteractionService.java | 13 +++++++++++++ src/com/android/launcher3/testing/TestProtocol.java | 1 + 2 files changed, 14 insertions(+) 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 22e689f7ba..51eaba0951 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -467,10 +467,17 @@ public class TouchInteractionService extends Service implements PluginListener Date: Tue, 2 Jun 2020 13:52:17 -0700 Subject: [PATCH 08/19] Hide OverviewActions when no tasks showing after launcher reset. Bug: 157709549 Change-Id: I1fe00a588a6cc5e840d38a08844017383c94fe68 --- .../src/com/android/quickstep/views/RecentsView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a506b7eb0c..b931653b48 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -492,6 +492,7 @@ public abstract class RecentsView extends PagedView impl public void init(OverviewActionsView actionsView) { mActionsView = actionsView; + mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0); } @Override From fad8a87773393e2033df9cabd0355563cad2536b Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Mon, 1 Jun 2020 16:29:53 -0700 Subject: [PATCH 09/19] Adds Overview Sandbox tutorial. Extracts fake task view animation logic to SwipeUpGestureTutorialController, which Home and Overview controllers extend. Fixes: 157945497 Change-Id: Ibd1c129c57e9bee8db62baae455ca1bd9df114c7 --- quickstep/res/values/strings.xml | 13 +- .../BackGestureTutorialController.java | 8 - .../HomeGestureTutorialController.java | 232 +---------------- .../OverviewGestureTutorialController.java | 124 +++++++++ .../OverviewGestureTutorialFragment.java | 37 +++ .../SwipeUpGestureTutorialController.java | 246 ++++++++++++++++++ .../interaction/TutorialController.java | 19 +- .../interaction/TutorialFragment.java | 3 + .../settings/DeveloperOptionsFragment.java | 9 + 9 files changed, 459 insertions(+), 232 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java create mode 100644 quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java create mode 100644 quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index be1d47b9f2..77345a0468 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -87,7 +87,6 @@ Predicted app: %1$s - Try the back gesture @@ -111,7 +110,6 @@ To change the sensitivity of the back gesture, go to Settings - Tutorial: Go Home @@ -123,6 +121,17 @@ Make sure you swipe straight up + + Tutorial: Switch Apps + + Swipe up from the bottom of the screen and hold + + Make sure you swipe from the bottom edge of the screen + + Try holding the window for longer before releasing + + Make sure you swipe straight up and pause + All set diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java index 41e86e00b6..1f398fc5ea 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java @@ -34,14 +34,6 @@ final class BackGestureTutorialController extends TutorialController { super(fragment, tutorialType); } - @Override - void transitToController() { - super.transitToController(); - if (mTutorialType != BACK_NAVIGATION_COMPLETE) { - showHandCoachingAnimation(); - } - } - @Override Integer getTitleStringId() { switch (mTutorialType) { diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java index 65f41a4a6c..0edabd45c5 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java @@ -15,143 +15,23 @@ */ package com.android.quickstep.interaction; -import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; -import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Insets; -import android.graphics.Outline; import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; import android.os.Build; -import android.view.SurfaceControl; import android.view.View; -import android.view.ViewOutlineProvider; -import android.view.WindowInsets.Type; -import android.view.WindowManager; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.PendingAnimation; -import com.android.quickstep.AnimatedFloat; -import com.android.quickstep.GestureState; -import com.android.quickstep.OverviewComponentObserver; -import com.android.quickstep.RecentsAnimationDeviceState; -import com.android.quickstep.SwipeUpAnimationLogic; -import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; -import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.TransformParams; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** A {@link TutorialController} for the Home tutorial. */ @TargetApi(Build.VERSION_CODES.R) -final class HomeGestureTutorialController extends TutorialController { - - private float mFakeTaskViewRadius; - private Rect mFakeTaskViewRect = new Rect(); - - private final ViewSwipeUpAnimation mViewSwipeUpAnimation; - private RunningWindowAnim mRunningWindowAnim; +final class HomeGestureTutorialController extends SwipeUpGestureTutorialController { HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); - - RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); - OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); - mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, - new GestureState(observer, -1)); - observer.onDestroy(); - deviceState.destroy(); - - DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) - .getDeviceProfile(mContext) - .copy(mContext); - Insets insets = mContext.getSystemService(WindowManager.class) - .getCurrentWindowMetrics() - .getWindowInsets() - .getInsets(Type.systemBars()); - dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); - mViewSwipeUpAnimation.initDp(dp); - - mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); - - mFakeTaskView.setClipToOutline(true); - mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); - } - }); - } - - private void cancelRunningAnimation() { - if (mRunningWindowAnim != null) { - mRunningWindowAnim.cancel(); - } - mRunningWindowAnim = null; - } - - /** Fades the task view, optionally after animating to a fake Overview. */ - private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { - cancelRunningAnimation(); - PendingAnimation anim = new PendingAnimation(300); - AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation, boolean isReverse) { - mFakeTaskView.setVisibility(View.INVISIBLE); - mFakeTaskView.setAlpha(1); - mRunningWindowAnim = null; - } - }; - if (toOverviewFirst) { - anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation, boolean isReverse) { - PendingAnimation fadeAnim = new PendingAnimation(300); - fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); - fadeAnim.addListener(resetTaskView); - AnimatorSet animset = fadeAnim.buildAnim(); - animset.setStartDelay(100); - animset.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(animset); - } - }); - } else { - anim.setViewAlpha(mFakeTaskView, 0, ACCEL); - anim.addListener(resetTaskView); - } - if (onEndRunnable != null) { - anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); - } - AnimatorSet animset = anim.buildAnim(); - animset.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(animset); - } - - @Override - void transitToController() { - super.transitToController(); - if (mTutorialType != HOME_NAVIGATION_COMPLETE) { - showHandCoachingAnimation(); - } } @Override @@ -190,6 +70,14 @@ final class HomeGestureTutorialController extends TutorialController { public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { case HOME_NAVIGATION: + switch (result) { + case BACK_COMPLETED_FROM_LEFT: + case BACK_COMPLETED_FROM_RIGHT: + case BACK_CANCELLED_FROM_LEFT: + case BACK_CANCELLED_FROM_RIGHT: + showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); + break; + } break; case HOME_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT @@ -206,17 +94,8 @@ final class HomeGestureTutorialController extends TutorialController { case HOME_NAVIGATION: switch (result) { case HOME_GESTURE_COMPLETED: { - hideFeedback(); - cancelRunningAnimation(); - hideHandCoachingAnimation(); - RectFSpringAnim rectAnim = - mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); - // After home animation finishes, fade out and then move to the next screen. - rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( - () -> fadeOutFakeTaskView(false, - () -> mTutorialFragment.changeController( - HOME_NAVIGATION_COMPLETE)))); - mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); + animateFakeTaskViewHome(finalVelocity, () -> + mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); break; } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: @@ -242,93 +121,4 @@ final class HomeGestureTutorialController extends TutorialController { } } - @Override - public void setNavBarGestureProgress(@Nullable Float displacement) { - if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE) { - mFakeTaskView.setVisibility(View.INVISIBLE); - } else { - mFakeTaskView.setVisibility(View.VISIBLE); - if (mRunningWindowAnim == null) { - mViewSwipeUpAnimation.updateDisplacement(displacement); - } - } - } - - private class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { - - ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, - GestureState gestureState) { - super(context, deviceState, gestureState, new FakeTransformParams()); - } - - void initDp(DeviceProfile dp) { - initTransitionEndpoints(dp); - mTaskViewSimulator.setPreviewBounds( - new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); - } - - @Override - public void updateFinalShift() { - float progress = mCurrentShift.value / mDragLengthFactor; - mWindowTransitionController.setPlayFraction(progress); - mTaskViewSimulator.apply(mTransformParams); - } - - AnimatedFloat getCurrentShift() { - return mCurrentShift; - } - - RectFSpringAnim handleSwipeUpToHome(PointF velocity) { - PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); - float currentShift = mCurrentShift.value; - final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y - * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); - float distanceToTravel = (1 - currentShift) * mTransitionDragLength; - - // we want the page's snap velocity to approximately match the velocity at - // which the user flings, so we scale the duration by a value near to the - // derivative of the scroll interpolator at zero, ie. 2. - long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); - long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); - HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { - @Override - public AnimatorPlaybackController createActivityAnimationToHome() { - return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); - } - - @NonNull - @Override - public RectF getWindowTargetRect() { - int fakeHomeIconSizePx = mDp.allAppsIconSizePx; - int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; - int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); - return new RectF(fakeHomeIconLeft, fakeHomeIconTop, - fakeHomeIconLeft + fakeHomeIconSizePx, - fakeHomeIconTop + fakeHomeIconSizePx); - } - }; - RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); - windowAnim.start(mContext, velocityPxPerMs); - return windowAnim; - } - } - - private class FakeTransformParams extends TransformParams { - - @Override - public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { - SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); - proxy.onBuildTargetParams(builder, null, this); - return new SurfaceParams[] {builder.build()}; - } - - @Override - public void applySurfaceParams(SurfaceParams[] params) { - SurfaceParams p = params[0]; - mFakeTaskView.setAnimationMatrix(p.matrix); - mFakeTaskViewRect.set(p.windowCrop); - mFakeTaskViewRadius = p.cornerRadius; - mFakeTaskView.invalidateOutline(); - } - } } diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java new file mode 100644 index 0000000000..c636ebaea9 --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + +import android.annotation.TargetApi; +import android.graphics.PointF; +import android.os.Build; +import android.view.View; + +import com.android.launcher3.R; +import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; +import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; + +/** A {@link TutorialController} for the Overview tutorial. */ +@TargetApi(Build.VERSION_CODES.R) +final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController { + + OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, + TutorialType tutorialType) { + super(fragment, tutorialType); + } + + @Override + Integer getTitleStringId() { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + return R.string.overview_gesture_tutorial_playground_title; + case OVERVIEW_NAVIGATION_COMPLETE: + return R.string.gesture_tutorial_confirm_title; + } + return null; + } + + @Override + Integer getSubtitleStringId() { + if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) { + return R.string.overview_gesture_tutorial_playground_subtitle; + } + return null; + } + + @Override + Integer getActionButtonStringId() { + if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { + return R.string.gesture_tutorial_action_button_label_done; + } + return null; + } + + @Override + void onActionButtonClicked(View button) { + mTutorialFragment.closeTutorial(); + } + + @Override + public void onBackGestureAttempted(BackGestureResult result) { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + switch (result) { + case BACK_COMPLETED_FROM_LEFT: + case BACK_COMPLETED_FROM_RIGHT: + case BACK_CANCELLED_FROM_LEFT: + case BACK_CANCELLED_FROM_RIGHT: + showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); + break; + } + break; + case OVERVIEW_NAVIGATION_COMPLETE: + if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT + || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) { + mTutorialFragment.closeTutorial(); + } + break; + } + } + + @Override + public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + switch (result) { + case HOME_GESTURE_COMPLETED: { + animateFakeTaskViewHome(finalVelocity, () -> + showFeedback(R.string.overview_gesture_feedback_home_detected)); + break; + } + case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: + case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: + showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); + break; + case OVERVIEW_GESTURE_COMPLETED: + fadeOutFakeTaskView(true, () -> + mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); + break; + case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: + case HOME_OR_OVERVIEW_CANCELLED: + fadeOutFakeTaskView(false, null); + showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction); + break; + } + break; + case OVERVIEW_NAVIGATION_COMPLETE: + if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) { + mTutorialFragment.closeTutorial(); + } + break; + } + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java new file mode 100644 index 0000000000..3357b70497 --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import com.android.launcher3.R; +import com.android.quickstep.interaction.TutorialController.TutorialType; + +/** Shows the Overview gesture interactive tutorial. */ +public class OverviewGestureTutorialFragment extends TutorialFragment { + @Override + int getHandAnimationResId() { + return R.drawable.overview_gesture; + } + + @Override + TutorialController createController(TutorialType type) { + return new OverviewGestureTutorialController(this, type); + } + + @Override + Class getControllerClass() { + return OverviewGestureTutorialController.class; + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java new file mode 100644 index 0000000000..14e00dce7a --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; +import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; +import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; +import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Insets; +import android.graphics.Outline; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.GestureState; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.SwipeUpAnimationLogic; +import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.TransformParams; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; + +@TargetApi(Build.VERSION_CODES.R) +abstract class SwipeUpGestureTutorialController extends TutorialController { + private final ViewSwipeUpAnimation mViewSwipeUpAnimation; + private float mFakeTaskViewRadius; + private Rect mFakeTaskViewRect = new Rect(); + private RunningWindowAnim mRunningWindowAnim; + + SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) { + super(tutorialFragment, tutorialType); + RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); + OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); + mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, + new GestureState(observer, -1)); + observer.onDestroy(); + deviceState.destroy(); + + DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) + .getDeviceProfile(mContext) + .copy(mContext); + Insets insets = mContext.getSystemService(WindowManager.class) + .getCurrentWindowMetrics() + .getWindowInsets() + .getInsets(WindowInsets.Type.systemBars()); + dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); + mViewSwipeUpAnimation.initDp(dp); + + mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); + mFakeTaskView.setClipToOutline(true); + mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); + } + }); + } + + private void cancelRunningAnimation() { + if (mRunningWindowAnim != null) { + mRunningWindowAnim.cancel(); + } + mRunningWindowAnim = null; + } + + /** Fades the task view, optionally after animating to a fake Overview. */ + void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { + hideFeedback(); + hideHandCoachingAnimation(); + cancelRunningAnimation(); + PendingAnimation anim = new PendingAnimation(300); + AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + mFakeTaskView.setVisibility(View.INVISIBLE); + mFakeTaskView.setAlpha(1); + mRunningWindowAnim = null; + } + }; + if (toOverviewFirst) { + anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + PendingAnimation fadeAnim = new PendingAnimation(300); + fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); + fadeAnim.addListener(resetTaskView); + AnimatorSet animset = fadeAnim.buildAnim(); + animset.setStartDelay(100); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); + } + }); + } else { + anim.setViewAlpha(mFakeTaskView, 0, ACCEL); + anim.addListener(resetTaskView); + } + if (onEndRunnable != null) { + anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); + } + AnimatorSet animset = anim.buildAnim(); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); + } + + void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) { + hideFeedback(); + hideHandCoachingAnimation(); + cancelRunningAnimation(); + RectFSpringAnim rectAnim = + mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); + // After home animation finishes, fade out and run onEndRunnable. + rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( + () -> fadeOutFakeTaskView(false, onEndRunnable))); + mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); + } + + @Override + public void setNavBarGestureProgress(@Nullable Float displacement) { + if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE + || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { + mFakeTaskView.setVisibility(View.INVISIBLE); + } else { + mFakeTaskView.setVisibility(View.VISIBLE); + if (mRunningWindowAnim == null) { + mViewSwipeUpAnimation.updateDisplacement(displacement); + } + } + } + + class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { + + ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState) { + super(context, deviceState, gestureState, new FakeTransformParams()); + } + + void initDp(DeviceProfile dp) { + initTransitionEndpoints(dp); + mTaskViewSimulator.setPreviewBounds( + new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); + } + + @Override + public void updateFinalShift() { + float progress = mCurrentShift.value / mDragLengthFactor; + mWindowTransitionController.setPlayFraction(progress); + mTaskViewSimulator.apply(mTransformParams); + } + + AnimatedFloat getCurrentShift() { + return mCurrentShift; + } + + RectFSpringAnim handleSwipeUpToHome(PointF velocity) { + PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); + float currentShift = mCurrentShift.value; + final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); + float distanceToTravel = (1 - currentShift) * mTransitionDragLength; + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 2. + long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); + HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); + } + + @NonNull + @Override + public RectF getWindowTargetRect() { + int fakeHomeIconSizePx = mDp.allAppsIconSizePx; + int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; + int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); + return new RectF(fakeHomeIconLeft, fakeHomeIconTop, + fakeHomeIconLeft + fakeHomeIconSizePx, + fakeHomeIconTop + fakeHomeIconSizePx); + } + }; + RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); + windowAnim.start(mContext, velocityPxPerMs); + return windowAnim; + } + } + + private class FakeTransformParams extends TransformParams { + + @Override + public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { + SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); + proxy.onBuildTargetParams(builder, null, this); + return new SurfaceParams[] {builder.build()}; + } + + @Override + public void applySurfaceParams(SurfaceParams[] params) { + SurfaceParams p = params[0]; + mFakeTaskView.setAnimationMatrix(p.matrix); + mFakeTaskViewRect.set(p.windowCrop); + mFakeTaskViewRadius = p.cornerRadius; + mFakeTaskView.invalidateOutline(); + } + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index f27d500eff..511c8b6dc6 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -140,6 +140,9 @@ abstract class TutorialController implements BackGestureAttemptCallback, void onActionTextButtonClicked(View button) {} void showHandCoachingAnimation() { + if (isComplete()) { + return; + } mHandCoachingAnimation.startLoopedAnimation(mTutorialType); } @@ -153,6 +156,12 @@ abstract class TutorialController implements BackGestureAttemptCallback, hideFeedback(); updateTitles(); updateActionButtons(); + + if (isComplete()) { + hideHandCoachingAnimation(); + } else { + showHandCoachingAnimation(); + } } private void updateTitles() { @@ -190,12 +199,20 @@ abstract class TutorialController implements BackGestureAttemptCallback, button.setOnClickListener(listener); } + private boolean isComplete() { + return mTutorialType == TutorialType.BACK_NAVIGATION_COMPLETE + || mTutorialType == TutorialType.HOME_NAVIGATION_COMPLETE + || mTutorialType == TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + } + /** Denotes the type of the tutorial. */ enum TutorialType { RIGHT_EDGE_BACK_NAVIGATION, LEFT_EDGE_BACK_NAVIGATION, BACK_NAVIGATION_COMPLETE, HOME_NAVIGATION, - HOME_NAVIGATION_COMPLETE + HOME_NAVIGATION_COMPLETE, + OVERVIEW_NAVIGATION, + OVERVIEW_NAVIGATION_COMPLETE } } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index a3881cffb0..da6815d776 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -68,6 +68,9 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener { case HOME_NAVIGATION: case HOME_NAVIGATION_COMPLETE: return new HomeGestureTutorialFragment(); + case OVERVIEW_NAVIGATION: + case OVERVIEW_NAVIGATION_COMPLETE: + return new OverviewGestureTutorialFragment(); default: Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name()); } diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java index 5bf91737c1..e80ee2cfe9 100644 --- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -237,6 +237,15 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { return true; }); sandboxCategory.addPreference(launchHomeTutorialPreference); + Preference launchOverviewTutorialPreference = new Preference(context); + launchOverviewTutorialPreference.setKey("launchOverviewTutorial"); + launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial"); + launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture"); + launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> { + startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION")); + return true; + }); + sandboxCategory.addPreference(launchOverviewTutorialPreference); } private String toName(String action) { From 1123ba3156f19e48ddec4f2ccf012c67293c2886 Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Mon, 1 Jun 2020 14:22:43 -0700 Subject: [PATCH 10/19] Gesture-excluding activities also exclude Assistant gesture. Bug: 157824552 Change-Id: Ie324ed86c8d7694fed263840975c02e676533ad1 --- .../quickstep/TouchInteractionService.java | 29 +++++++++++-------- .../RecentsAnimationDeviceState.java | 8 +++-- 2 files changed, 22 insertions(+), 15 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 51eaba0951..412a05a2c5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -30,6 +30,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.Service; @@ -487,18 +488,22 @@ public class TouchInteractionService extends Service implements PluginListener Date: Tue, 2 Jun 2020 18:45:20 -0700 Subject: [PATCH 11/19] More logging for swiping to home not working Bug: 158017601 Change-Id: Ic967e39c6521bbf100015495970fdbfb67c4ea09 --- .../com/android/quickstep/OrientationTouchTransformer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index a976126202..c3ff32f58a 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -36,6 +36,7 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DefaultDisplay; import java.io.PrintWriter; @@ -246,6 +247,10 @@ class OrientationTouchTransformer { } boolean touchInValidSwipeRegions(float x, float y) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "touchInValidSwipeRegions " + x + "," + y + " in " + + mLastRectTouched); + } if (mLastRectTouched != null) { return mLastRectTouched.contains(x, y); } From 9752dedd71529d456207c2ca3218b38d5b75343e Mon Sep 17 00:00:00 2001 From: thiruram Date: Tue, 2 Jun 2020 11:32:51 -0700 Subject: [PATCH 12/19] Logs tap or longpress on task icon. Sample Log: https://docs.google.com/document/d/1FXi_jY1B63aMSjNyomAmoN1nSTtVbvculNGX7TKdRRw/edit#bookmark=id.psxq3ooyppff Bug: 157770913 Change-Id: I021b07fa8b3953a0f9a5e5ad9e4ee1c55d98d836 --- .../src/com/android/quickstep/views/TaskView.java | 2 ++ src/com/android/launcher3/logging/StatsLogManager.java | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 6b759ba43a..3a8fcf7664 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -30,6 +30,7 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import android.animation.Animator; @@ -424,6 +425,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private boolean showTaskMenu(int action) { getRecentsView().snapToPage(getRecentsView().indexOfChild(this)); mMenuView = TaskMenuView.showForTask(this); + mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto()); UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE, LauncherLogProto.ItemType.TASK_ICON); if (mMenuView != null) { diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index dda39e8a51..9d94df4c22 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -102,7 +102,11 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User opened app info of the package by tapping on appinfo system shortcut " + "within longpress popup window.") - LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515); + LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515), + + @UiEvent(doc = "User tapped or long pressed on the task icon(aka package icon) " + + "from overview to open task menu.") + LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS(517); // ADD MORE private final int mId; From b812b3ff608a95a57cc6c122b822e0b8c2adfbb7 Mon Sep 17 00:00:00 2001 From: thiruram Date: Tue, 2 Jun 2020 15:40:25 -0700 Subject: [PATCH 13/19] Log tapping on split screen & free form icons within task menu. Bug: 157770913 Sample Log: https://docs.google.com/document/d/1FXi_jY1B63aMSjNyomAmoN1nSTtVbvculNGX7TKdRRw/edit#bookmark=id.4hmzxwn1efy Change-Id: Id98dccb9452af7be20f636410d886dc8aeaa6bd1 --- .../quickstep/TaskShortcutFactory.java | 26 ++++++++++++------- .../com/android/quickstep/views/TaskView.java | 4 +-- .../launcher3/logging/StatsLogManager.java | 12 ++++++--- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java index 021d39dd6e..cdaa6555a0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java @@ -18,6 +18,8 @@ package com.android.quickstep; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP; import android.app.Activity; @@ -35,6 +37,7 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.logging.StatsLogManager.LauncherEvent; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -83,10 +86,12 @@ public interface TaskShortcutFactory { private final int mIconRes; private final int mTextRes; + private final LauncherEvent mLauncherEvent; - MultiWindowFactory(int iconRes, int textRes) { + MultiWindowFactory(int iconRes, int textRes, LauncherEvent launcherEvent) { mIconRes = iconRes; mTextRes = textRes; + mLauncherEvent = launcherEvent; } protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId); @@ -102,7 +107,8 @@ public interface TaskShortcutFactory { if (!isAvailable(activity, task.key.displayId)) { return null; } - return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this); + return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this, + mLauncherEvent); } } @@ -114,11 +120,12 @@ public interface TaskShortcutFactory { private final TaskThumbnailView mThumbnailView; private final TaskView mTaskView; private final MultiWindowFactory mFactory; + private final LauncherEvent mLauncherEvent; - public MultiWindowSystemShortcut(int iconRes, int textRes, - BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) { + public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity, + TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) { super(iconRes, textRes, activity, dummyInfo(taskView)); - + mLauncherEvent = launcherEvent; mHandler = new Handler(Looper.getMainLooper()); mTaskView = taskView; mRecentsView = activity.getOverviewPanel(); @@ -203,12 +210,13 @@ public interface TaskShortcutFactory { WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( future, animStartedListener, mHandler, true /* scaleUp */, taskKey.displayId); + mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto()); } } } - TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory( - R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) { + TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen, + R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) { @Override protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { @@ -241,8 +249,8 @@ public interface TaskShortcutFactory { } }; - TaskShortcutFactory FREE_FORM = new MultiWindowFactory( - R.drawable.ic_split_screen, R.string.recent_task_option_freeform) { + TaskShortcutFactory FREE_FORM = new MultiWindowFactory(R.drawable.ic_split_screen, + R.string.recent_task_option_freeform, LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP) { @Override protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 3a8fcf7664..af9d7f76bc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -223,8 +223,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { setOutlineProvider(mOutlineProvider); } - /* Builds proto for logging */ - protected LauncherAtom.ItemInfo buildProto() { + /** Builds proto for logging */ + public LauncherAtom.ItemInfo buildProto() { ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key); LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder(); itemBuilder.setIsWork(componentKey.user != Process.myUserHandle()); diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 9d94df4c22..85013d7216 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -96,6 +96,10 @@ public class StatsLogManager implements ResourceBasedOverride { + "the icon onto 'Uninstall' button in the target bar") LAUNCHER_ITEM_UNINSTALL_CANCELLED(470), + @UiEvent(doc = "User tapped or long pressed on the task icon(aka package icon) " + + "from overview to open task menu.") + LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS(517), + @UiEvent(doc = "User opened package specific widgets list by tapping on widgets system " + "shortcut within longpress popup window.") LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP(514), @@ -104,9 +108,11 @@ public class StatsLogManager implements ResourceBasedOverride { + "within longpress popup window.") LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515), - @UiEvent(doc = "User tapped or long pressed on the task icon(aka package icon) " - + "from overview to open task menu.") - LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS(517); + @UiEvent(doc = "User tapped on split screen icon on a task menu.") + LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP(518), + + @UiEvent(doc = "User tapped on free form icon on a task menu.") + LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP(519); // ADD MORE private final int mId; From 1e60bb54ca09e26a632a7484a7846be799ff1141 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Wed, 3 Jun 2020 10:41:48 -0700 Subject: [PATCH 14/19] Shared Lib - add stub for handleImageBundleAsScreenshot. Bug: 156757117 Test: local compile Change-Id: I2e249055cc8f917b169943569e0c624df823ffb3 --- quickstep/src/com/android/quickstep/SystemUiProxy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 20d133c3c6..c727da925a 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -31,6 +31,7 @@ import android.view.MotionEvent; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.recents.model.Task; /** * Holds the reference to SystemUI. @@ -344,4 +345,8 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, + Insets visibleInsets, Task.TaskKey task) { + } } From f665787d0da5231203f9c8a8e7bba50912aeb285 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 3 Jun 2020 13:07:25 -0700 Subject: [PATCH 15/19] Only animating QSB if it is visible when going home Bug: 154863593 Change-Id: Icee925578985639e1117ff624a422082123bea1e --- .../android/quickstep/util/StaggeredWorkspaceAnim.java | 7 +++++-- src/com/android/launcher3/allapps/SearchUiManager.java | 9 +++++++++ .../allapps/search/AppsSearchContainerLayout.java | 3 +-- 3 files changed, 15 insertions(+), 4 deletions(-) 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 ebc9f96394..70903935bf 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 @@ -121,8 +121,11 @@ public class StaggeredWorkspaceAnim { addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows); } - View qsb = launcher.findViewById(R.id.search_container_all_apps); - addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows); + if (launcher.getAppsView().getSearchUiManager() + .isQsbVisible(NORMAL.getVisibleElements(launcher))) { + addStaggeredAnimationForView(launcher.getAppsView().getSearchView(), + grid.inv.numRows + 2, totalRows); + } } if (animateOverviewScrim) { diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java index 34bf636cb4..7d5363f269 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.LauncherState.ALL_APPS_HEADER; + import android.graphics.Rect; import android.view.KeyEvent; import android.view.animation.Interpolator; @@ -56,6 +58,13 @@ public interface SearchUiManager { void setContentVisibility(int visibleElements, PropertySetter setter, Interpolator interpolator); + /** + * Returns true if the QSB should be visible for the given set of visible elements + */ + default boolean isQsbVisible(int visibleElements) { + return (visibleElements & ALL_APPS_HEADER) != 0; + } + /** * Called to control how the search UI result should be handled. * diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index e72e1a8bc1..356c52ca39 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -19,7 +19,6 @@ import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.getSize; import static android.view.View.MeasureSpec.makeMeasureSpec; -import static com.android.launcher3.LauncherState.ALL_APPS_HEADER; import static com.android.launcher3.Utilities.prefixTextWithIcon; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; @@ -213,7 +212,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText @Override public void setContentVisibility(int visibleElements, PropertySetter setter, Interpolator interpolator) { - setter.setViewAlpha(this, (visibleElements & ALL_APPS_HEADER) != 0 ? 1 : 0, interpolator); + setter.setViewAlpha(this, isQsbVisible(visibleElements) ? 1 : 0, interpolator); } @Override From f667a1352d7684e76ae1ca2ae410968dae49320c Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Fri, 29 May 2020 14:47:42 -0700 Subject: [PATCH 16/19] Restore hotseat when user turns off suggestions immediately after migration - Creates a backup table `hybrid_hotseat_restore` and copies current workspace layout before hotseat migration - restores to back up when user turns off suggestions and launcher receives empty predicted items - deletes hybrid_hotseat_restore table if there's a layout change Test: Manual Bug: 157688471 Change-Id: Iaf7ddb33799493d36dbcd12408b57224162221d9 --- .../hybridhotseat/HotseatEduController.java | 8 +- .../hybridhotseat/HotseatEduDialog.java | 3 +- .../HotseatPredictionController.java | 10 +- .../hybridhotseat/HotseatRestoreHelper.java | 100 ++++++++++++++++++ quickstep/res/values/strings.xml | 2 + .../android/launcher3/LauncherProvider.java | 12 +++ .../android/launcher3/LauncherSettings.java | 7 ++ .../launcher3/model/GridBackupTable.java | 26 +++++ 8 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index 7c4f3ecd01..f1ce72e708 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -52,6 +52,7 @@ public class HotseatEduController { private final Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; private List mPredictedApps; private HotseatEduDialog mActiveDialog; @@ -59,9 +60,10 @@ public class HotseatEduController { private IntArray mNewScreens = null; private Runnable mOnOnboardingComplete; - HotseatEduController(Launcher launcher, Runnable runnable) { + HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) { mLauncher = launcher; mHotseat = launcher.getHotseat(); + mRestoreHelper = restoreHelper; mOnOnboardingComplete = runnable; } @@ -69,11 +71,14 @@ public class HotseatEduController { * Checks what type of migration should be used and migrates hotseat */ void migrate() { + mRestoreHelper.createBackup(); if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) { migrateToFolder(); } else { migrateHotseatWhole(); } + Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off, + null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION))); } /** @@ -84,7 +89,6 @@ public class HotseatEduController { */ private int migrateToFolder() { ArrayDeque folders = new ArrayDeque<>(); - ArrayList putIntoFolder = new ArrayList<>(); //separate folders and items that can get in folders diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index fa137f814c..12d537cb28 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -16,7 +16,8 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; +import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType + .HYBRID_HOTSEAT_CANCELED; import android.animation.PropertyValuesHolder; import android.content.Context; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 05bcb57adc..725f516335 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -92,6 +92,8 @@ public class HotseatPredictionController implements DragController.DragListener, private Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; + private List mComponentKeyMappers = new ArrayList<>(); private DynamicItemCache mDynamicItemCache; @@ -129,6 +131,7 @@ public class HotseatPredictionController implements DragController.DragListener, mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons; launcher.getDeviceProfile().inv.addOnChangeListener(this); mHotseat.addOnAttachStateChangeListener(this); + mRestoreHelper = new HotseatRestoreHelper(mLauncher); if (mHotseat.isAttachedToWindow()) { onViewAttachedToWindow(mHotseat); } @@ -297,7 +300,8 @@ public class HotseatPredictionController implements DragController.DragListener, }); setPauseUIUpdate(false); if (!isEduSeen()) { - mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); + mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper, + this::createPredictor); } } @@ -320,9 +324,11 @@ public class HotseatPredictionController implements DragController.DragListener, updateDependencies(); bindItems(items, false, null); } - private void setPredictedApps(List appTargets) { mComponentKeyMappers.clear(); + if (appTargets.isEmpty() && mRestoreHelper.shouldRestoreToBackup()) { + mRestoreHelper.restoreBackup(); + } StringBuilder predictionLog = new StringBuilder("predictedApps: [\n"); ArrayList componentKeys = new ArrayList<>(); for (AppTarget appTarget : appTargets) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java new file mode 100644 index 0000000000..c95ff7a2d1 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.hybridhotseat; + +import static com.android.launcher3.LauncherSettings.Favorites.HYBRID_HOTSEAT_BACKUP_TABLE; +import static com.android.launcher3.provider.LauncherDbUtils.tableExists; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.GridBackupTable; +import com.android.launcher3.provider.LauncherDbUtils; + +/** + * A helper class to manage migration revert restoration for hybrid hotseat + */ +public class HotseatRestoreHelper { + private final Launcher mLauncher; + private boolean mBackupExists; + + HotseatRestoreHelper(Launcher context) { + mLauncher = context; + setupBackupTable(); + } + + /** + * Creates a snapshot backup of Favorite table for future restoration use. + */ + public synchronized void createBackup() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; + GridBackupTable backupTable = new GridBackupTable(mLauncher, + transaction.getDb(), idp.numHotseatIcons, idp.numColumns, + idp.numRows); + backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE); + transaction.commit(); + LauncherSettings.Settings.call(mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE); + mBackupExists = true; + } + } + + /** + * Finds and restores a previously saved snapshow of Favorites table + */ + public void restoreBackup() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) { + mBackupExists = false; + return; + } + InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; + GridBackupTable backupTable = new GridBackupTable(mLauncher, + transaction.getDb(), idp.numHotseatIcons, idp.numColumns, + idp.numRows); + backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); + transaction.commit(); + mBackupExists = false; + mLauncher.getModel().forceReload(); + } + } + + /** + * Returns if prediction controller should attempt restoring a backup + */ + public synchronized boolean shouldRestoreToBackup() { + return mBackupExists; + } + + private synchronized void setupBackupTable() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + mBackupExists = tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE); + } + } +} diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index be1d47b9f2..ac7ff2a1ed 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -83,6 +83,8 @@ Drag apps off the bottom row to get app suggestions App suggestions added to empty space + + App suggestions Enabled Predicted app: %1$s diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 48b97fafd3..e8b5568783 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -411,6 +411,11 @@ public class LauncherProvider extends ContentProvider { Favorites.BACKUP_TABLE_NAME); return null; } + case LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE: { + mOpenHelper.mHotseatRestoreTableExists = tableExists( + mOpenHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE); + return null; + } case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: { final long ts = System.currentTimeMillis(); if (ts - mLastRestoreTimestamp > RESTORE_BACKUP_TABLE_DELAY) { @@ -609,6 +614,7 @@ public class LauncherProvider extends ContentProvider { private int mMaxItemId = -1; private int mMaxScreenId = -1; private boolean mBackupTableExists; + private boolean mHotseatRestoreTableExists; static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) { return createDatabaseHelper(context, null, forMigration); @@ -633,6 +639,8 @@ public class LauncherProvider extends ContentProvider { databaseHelper.mBackupTableExists = tableExists( databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME); } + databaseHelper.mHotseatRestoreTableExists = tableExists( + databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE); databaseHelper.initIds(); return databaseHelper; @@ -679,6 +687,10 @@ public class LauncherProvider extends ContentProvider { dropTable(db, Favorites.BACKUP_TABLE_NAME); mBackupTableExists = false; } + if (mHotseatRestoreTableExists) { + dropTable(db, Favorites.HYBRID_HOTSEAT_BACKUP_TABLE); + mHotseatRestoreTableExists = false; + } } /** diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 67890726b1..c30c401c94 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -99,6 +99,11 @@ public class LauncherSettings { */ public static final String BACKUP_TABLE_NAME = "favorites_bakup"; + /** + * Backup table created when user hotseat is moved to workspace for hybrid hotseat + */ + public static final String HYBRID_HOTSEAT_BACKUP_TABLE = "hotseat_restore_backup"; + /** * Temporary table used specifically for grid migrations during wallpaper preview */ @@ -332,6 +337,8 @@ public class LauncherSettings { public static final String METHOD_REFRESH_BACKUP_TABLE = "refresh_backup_table"; + public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table"; + public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table"; public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper"; diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java index 4a1bc4df88..acfc339268 100644 --- a/src/com/android/launcher3/model/GridBackupTable.java +++ b/src/com/android/launcher3/model/GridBackupTable.java @@ -127,6 +127,32 @@ public class GridBackupTable { return mRestoredHotseatSize; } + /** + * Creates a new table and populates with copy of Favorites.TABLE_NAME + */ + public void createCustomBackupTable(String tableName) { + long profileId = UserCache.INSTANCE.get(mContext).getSerialNumberForUser( + Process.myUserHandle()); + copyTable(mDb, Favorites.TABLE_NAME, tableName, profileId); + encodeDBProperties(0); + } + + /** + * + * Restores the contents of a custom table to Favorites.TABLE_NAME + */ + + public void restoreFromCustomBackupTable(String tableName, boolean dropAfterUse) { + if (!tableExists(mDb, tableName)) { + return; + } + long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser( + Process.myUserHandle()); + copyTable(mDb, tableName, Favorites.TABLE_NAME, userSerial); + if (dropAfterUse) { + dropTable(mDb, tableName); + } + } /** * Copy valid grid entries from one table to another. */ From 3212b10941eb1fd925050d45755368f27e4b00ef Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Wed, 3 Jun 2020 13:48:39 -0700 Subject: [PATCH 17/19] Adds Dark mode support to Sandbox. Screenshots Back: https://drive.google.com/open?id=1brE9WLWGeKaOgzMsczpV40rPIckOk1YJ Home: https://drive.google.com/open?id=1zexyFPsXr4VzvwlMqhLv720kqHh7s66v Tutorial complete: https://drive.google.com/open?id=1UbeIDYMQR04lzJhGK7rp2rSdmhiHl5QR Fixes: 157483039 Change-Id: Id9753a00832dbe42316fbdcaf447f3a51a45bc3e --- quickstep/res/layout/gesture_tutorial_fragment.xml | 3 ++- quickstep/res/values/colors.xml | 1 + quickstep/res/values/styles.xml | 6 +++--- .../android/quickstep/interaction/EdgeBackGesturePanel.java | 6 +++++- res/drawable/gesture_tutorial_ripple.xml | 5 +---- res/values/colors.xml | 6 ------ res/values/styles.xml | 1 - 7 files changed, 12 insertions(+), 16 deletions(-) diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 74197bef40..459d65faf7 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -16,7 +16,7 @@ + android:background="?android:attr/colorBackground"> + #FFFFFFFF #99000000 \ No newline at end of file diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index 90957e42ff..8d054b4be5 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -35,14 +35,14 @@ @@ -50,7 +50,7 @@ diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 5bf50260aa..0521db4d1b 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -280,7 +280,11 @@ public class EdgeBackGesturePanel extends View { new SpringForce() .setStiffness(SpringForce.STIFFNESS_MEDIUM) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); - mPaint.setColor(context.getColor(R.color.back_arrow_color_dark)); + int currentNightMode = + context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + mPaint.setColor(context.getColor(currentNightMode == Configuration.UI_MODE_NIGHT_YES + ? R.color.back_arrow_color_light + : R.color.back_arrow_color_dark)); loadDimens(); updateArrowDirection(); diff --git a/res/drawable/gesture_tutorial_ripple.xml b/res/drawable/gesture_tutorial_ripple.xml index ca456627c5..782af33ab3 100644 --- a/res/drawable/gesture_tutorial_ripple.xml +++ b/res/drawable/gesture_tutorial_ripple.xml @@ -1,6 +1,3 @@ - - \ No newline at end of file + xmlns:android="http://schemas.android.com/apk/res/android"/> \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index c4ec7dd7f0..043ad9a9d5 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -38,14 +38,8 @@ #E5E5E5 #9AA0A6 - #FFFFFFFF - #FF000000 - #99000000 - #FF000000 #A0C2F9 #6DA1FF #FFFFFFFF #1A73E8 - - diff --git a/res/values/styles.xml b/res/values/styles.xml index 26b72052fd..e470c42459 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -81,7 +81,6 @@ #CDFFFFFF #FF80868B ?attr/workspaceTextColor -