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 66c60bca1f..fdd3562a2e 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/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 a487869316..79dc3e25c3 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 @@ -37,6 +37,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; @@ -210,6 +211,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/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..6cbe794860 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) -> { @@ -356,7 +357,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()); } @@ -364,6 +366,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/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index 62eb235251..4801d71779 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -310,8 +310,8 @@ public final class LauncherActivityInterface extends @Override protected float getExtraSpace(Context context, DeviceProfile dp, PagedOrientationHandler orientationHandler) { - if (dp.isVerticalBarLayout() || - hideShelfInTwoButtonLandscape(context, orientationHandler)) { + if ((dp.isVerticalBarLayout() && !showOverviewActions(context)) + || hideShelfInTwoButtonLandscape(context, orientationHandler)) { return 0; } else { Resources res = context.getResources(); @@ -319,12 +319,14 @@ public final class LauncherActivityInterface extends //TODO: this needs to account for the swipe gesture height and accessibility // UI when shown. float actionsBottomMargin = 0; - if (getMode(context) == Mode.THREE_BUTTONS) { - actionsBottomMargin = res.getDimensionPixelSize( + if (!dp.isVerticalBarLayout()) { + if (getMode(context) == Mode.THREE_BUTTONS) { + actionsBottomMargin = res.getDimensionPixelSize( R.dimen.overview_actions_bottom_margin_three_button); - } else { - actionsBottomMargin = res.getDimensionPixelSize( + } else { + actionsBottomMargin = res.getDimensionPixelSize( R.dimen.overview_actions_bottom_margin_gesture); + } } float actionsHeight = actionsBottomMargin + res.getDimensionPixelSize(R.dimen.overview_actions_height); 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/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/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 22e689f7ba..6f596e9e08 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; @@ -134,10 +135,10 @@ public class TouchInteractionService extends Service implements PluginListener 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/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/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/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java index 83287c463a..08c3dc9c27 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java @@ -21,6 +21,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; import android.content.Context; +import android.content.res.Configuration; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -33,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.R; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks; @@ -129,6 +131,12 @@ public class OverviewActionsView extends FrameLayo updateHiddenFlags(HIDDEN_UNSUPPORTED_NAVIGATION, !removeShelfFromOverview(getContext())); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateVerticalMargin(SysUINavigationMode.getMode(getContext())); + } + public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) { if (enable) { mHiddenFlags |= visibilityFlags; @@ -152,10 +160,13 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA); } - /** Updates vertical margins for different navigation mode. */ - public void updateVerticalMarginForNavModeChange(Mode mode) { - int bottomMargin = 0; - if (mode == Mode.THREE_BUTTONS) { + /** Updates vertical margins for different navigation mode or configuration changes. */ + public void updateVerticalMargin(Mode mode) { + int bottomMargin; + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + bottomMargin = 0; + } else if (mode == Mode.THREE_BUTTONS) { bottomMargin = getResources() .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_three_button); } else { 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 324aaecf0e..64d90cf4f7 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; @@ -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 @@ -501,7 +502,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); @@ -1626,8 +1627,10 @@ public abstract class RecentsView extends PagedView impl : View.LAYOUT_DIRECTION_RTL); mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated()); mActivity.getDragLayer().recreateControllers(); + boolean isInLandscape = touchRotation != 0 + || mOrientationState.getLauncherRotation() != ROTATION_0; mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, - touchRotation != 0 || mOrientationState.getLauncherRotation() != ROTATION_0); + !mOrientationState.canLauncherRotate() && isInLandscape); resetPaddingFromTaskSize(); requestLayout(); } 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..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 @@ -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; @@ -222,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()); @@ -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/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/strings.xml b/quickstep/res/values/strings.xml index 37d5ca3715..1ed3b2457c 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 @@ -110,7 +112,6 @@ To change the sensitivity of the back gesture, go to Settings - Tutorial: Go Home @@ -122,6 +123,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/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/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 0968d8e4ad..48743070d8 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -93,7 +93,7 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onNavigationModeChanged(Mode newMode) { getDragLayer().recreateControllers(); if (mActionsView != null && isOverviewActionsEnabled()) { - mActionsView.updateVerticalMarginForNavModeChange(newMode); + mActionsView.updateVerticalMargin(newMode); } } @@ -175,7 +175,7 @@ public abstract class BaseQuickstepLauncher extends Launcher // Overview is above all other launcher elements, including qsb, so move it to the top. getOverviewPanel().bringToFront(); mActionsView.bringToFront(); - mActionsView.updateVerticalMarginForNavModeChange(SysUINavigationMode.getMode(this)); + mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); } } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index c0d16c9ba8..10f789d428 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. @@ -714,8 +714,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 : @@ -743,8 +742,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/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); } diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index fcffaedfd8..ebe9e26c02 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; @@ -191,11 +192,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/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index a976126202..3375c53124 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); } @@ -287,10 +292,16 @@ class OrientationTouchTransformer { for (int i = 0; i < MAX_ORIENTATIONS; i++) { OrientationRectF rect = mSwipeTouchRegions.get(i); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "transform:DOWN, rect=" + rect); + } if (rect == null) { continue; } if (rect.applyTransform(event, false)) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "setting mLastRectTouched"); + } mLastRectTouched = rect; mLastRectRotation = rect.mRotation; if (mEnableMultipleRegions && mCurrentDisplayRotation == mLastRectRotation) { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 7f9a8bd699..66e011d067 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -525,13 +525,15 @@ public class RecentsAnimationDeviceState implements /** * @param ev An ACTION_DOWN motion event - * @return whether the given motion event can trigger the assistant. + * @param task Info for the currently running task + * @return whether the given motion event can trigger the assistant over the current task. */ - public boolean canTriggerAssistantAction(MotionEvent ev) { + public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) { return mAssistantAvailable && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) && mOrientationTouchTransformer.touchInAssistantRegion(ev) - && !isLockToAppActive(); + && !isLockToAppActive() + && !isGestureBlockedActivity(task); } /** 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/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/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/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/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 -