diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 475f06109e..ecd38b486c 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -37,7 +37,9 @@ import android.content.IntentSender; import android.content.ServiceConnection; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.Handler; import android.os.IBinder; +import android.util.Log; import android.view.View; import android.window.SplashScreen; @@ -89,6 +91,11 @@ import java.util.stream.Stream; public abstract class BaseQuickstepLauncher extends Launcher implements NavigationModeChangeListener { + private static final long BACKOFF_MILLIS = 1000; + + // Max backoff caps at 5 mins + private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000; + private DepthController mDepthController = new DepthController(this); private QuickstepTransitionManager mAppTransitionManager; @@ -108,12 +115,24 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager(); mTaskbarManager.setLauncher(BaseQuickstepLauncher.this); + Log.d(TAG, "TIS service connected"); + resetServiceBindRetryState(); } @Override public void onServiceDisconnected(ComponentName componentName) { } + + @Override + public void onBindingDied(ComponentName name) { + Log.w(TAG, "TIS binding died"); + internalBindToTIS(); + } }; + + private final Runnable mConnectionRunnable = this::internalBindToTIS; + private short mConnectionAttempts; private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this); + private final Handler mHandler = new Handler(); // Will be updated when dragging from taskbar. private @Nullable DragOptions mNextWorkspaceDragOptions = null; @@ -132,11 +151,11 @@ public abstract class BaseQuickstepLauncher extends Launcher SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); - unbindService(mTisBinderConnection); if (mTaskbarManager != null) { mTaskbarManager.clearLauncher(this); } + resetServiceBindRetryState(); super.onDestroy(); } @@ -264,8 +283,33 @@ public abstract class BaseQuickstepLauncher extends Launcher mAppTransitionManager = new QuickstepTransitionManager(this); mAppTransitionManager.registerRemoteAnimations(); - bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0); + internalBindToTIS(); + } + /** + * Binds {@link #mTisBinderConnection} to {@link TouchInteractionService}. If the binding fails, + * attempts to retry via {@link #mConnectionRunnable} + */ + private void internalBindToTIS() { + boolean bound = bindService(new Intent(this, TouchInteractionService.class), + mTisBinderConnection, 0); + if (bound) { + resetServiceBindRetryState(); + return; + } + + Log.w(TAG, "Retrying TIS Binder connection attempt: " + mConnectionAttempts); + final long timeoutMs = (long) Math.min( + Math.scalb(BACKOFF_MILLIS, mConnectionAttempts), MAX_BACKOFF_MILLIS); + mHandler.postDelayed(mConnectionRunnable, timeoutMs); + mConnectionAttempts++; + } + + private void resetServiceBindRetryState() { + if (mHandler.hasCallbacks(mConnectionRunnable)) { + mHandler.removeCallbacks(mConnectionRunnable); + } + mConnectionAttempts = 0; } public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) { diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index c04d15ffe4..096ac6c5ca 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -387,8 +387,11 @@ public abstract class AbsSwipeUpHandler, mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> { ThumbnailData snapshot = mGestureState.consumeRecentsAnimationCanceledSnapshot(); if (snapshot != null) { - mRecentsView.switchToScreenshot(snapshot, - () -> mRecentsAnimationController.cleanupScreenshot()); + mRecentsView.switchToScreenshot(snapshot, () -> { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.cleanupScreenshot(); + } + }); mRecentsView.onRecentsAnimationComplete(); } }); @@ -740,6 +743,8 @@ public abstract class AbsSwipeUpHandler, mRecentsAnimationStartCallbacks.clear(); } + TaskViewUtils.setDividerBarShown(mRecentsAnimationTargets.nonApps, false); + // Only add the callback to enable the input consumer after we actually have the controller mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED, mRecentsAnimationController::enableInputConsumer); @@ -754,6 +759,8 @@ public abstract class AbsSwipeUpHandler, mActivityInitListener.unregister(); mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); + TaskViewUtils.setDividerBarShown(mRecentsAnimationTargets.nonApps, true); + // Defer clearing the controller and the targets until after we've updated the state mRecentsAnimationController = null; mRecentsAnimationTargets = null; @@ -882,6 +889,7 @@ public abstract class AbsSwipeUpHandler, break; case LAST_TASK: mStateCallback.setState(STATE_RESUME_LAST_TASK); + TaskViewUtils.setDividerBarShown(mRecentsAnimationTargets.nonApps, true); break; } ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget); @@ -1723,6 +1731,9 @@ public abstract class AbsSwipeUpHandler, @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { + if (!controller.getFinishTargetIsLauncher()) { + TaskViewUtils.setDividerBarShown(mRecentsAnimationTargets.nonApps, true); + } mRecentsAnimationController = null; mRecentsAnimationTargets = null; if (mRecentsView != null) { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index a21c7140de..239233b191 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -19,6 +19,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.graphics.Rect; import android.util.ArraySet; +import android.view.RemoteAnimationTarget; import androidx.annotation.BinderThread; import androidx.annotation.UiThread; @@ -39,6 +40,7 @@ public class RecentsAnimationCallbacks implements com.android.systemui.shared.system.RecentsAnimationListener { private final Set mListeners = new ArraySet<>(); + private final SystemUiProxy mSystemUiProxy; private final boolean mAllowMinimizeSplitScreen; // TODO(141886704): Remove these references when they are no longer needed @@ -46,7 +48,9 @@ public class RecentsAnimationCallbacks implements private boolean mCancelled; - public RecentsAnimationCallbacks(boolean allowMinimizeSplitScreen) { + public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy, + boolean allowMinimizeSplitScreen) { + mSystemUiProxy = systemUiProxy; mAllowMinimizeSplitScreen = allowMinimizeSplitScreen; } @@ -89,8 +93,11 @@ public class RecentsAnimationCallbacks implements RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, Rect homeContentInsets, Rect minimizedHomeBounds) { + RemoteAnimationTarget[] nonAppTargets = + mSystemUiProxy.onGoingToRecentsLegacy(mCancelled); RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets, - wallpaperTargets, homeContentInsets, minimizedHomeBounds); + wallpaperTargets, RemoteAnimationTargetCompat.wrap(nonAppTargets), + homeContentInsets, minimizedHomeBounds); mController = new RecentsAnimationController(animationController, mAllowMinimizeSplitScreen, this::onAnimationFinished); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java index 3861bab04b..b6d9016727 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -31,9 +31,9 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets { public final Rect minimizedHomeBounds; public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps, - RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets, - Rect minimizedHomeBounds) { - super(apps, wallpapers, new RemoteAnimationTargetCompat[0], MODE_CLOSING); + RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps, + Rect homeContentInsets, Rect minimizedHomeBounds) { + super(apps, wallpapers, nonApps, MODE_CLOSING); this.homeContentInsets = homeContentInsets; this.minimizedHomeBounds = minimizedHomeBounds; } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 090fd015a4..d6b95c13ab 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -33,6 +33,8 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.MotionEvent; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import com.android.launcher3.util.MainThreadInitializedObject; @@ -562,6 +564,22 @@ public class SystemUiProxy implements ISystemUiProxy, } } + /** + * Start multiple tasks in split-screen simultaneously. + */ + public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId, + Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition, + RemoteAnimationAdapter adapter) { + if (mSystemUiProxy != null) { + try { + mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId, + sideOptions, sidePosition, adapter); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startTasksWithLegacyTransition"); + } + } + } + public void startShortcut(String packageName, String shortcutId, int stage, int position, Bundle options, UserHandle user) { if (mSplitScreen != null) { @@ -595,6 +613,24 @@ public class SystemUiProxy implements ISystemUiProxy, } } + /** + * Call this when going to recents so that shell can set-up and provide appropriate leashes + * for animation (eg. DividerBar). + * + * @param cancel true if recents starting is being cancelled. + * @return RemoteAnimationTargets of windows that need to animate but only exist in shell. + */ + public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) { + if (mSplitScreen != null) { + try { + return mSplitScreen.onGoingToRecentsLegacy(cancel); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onGoingToRecentsLegacy"); + } + } + return null; + } + // // One handed // diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 33718a3cac..fe07cbdcce 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -109,7 +109,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn final BaseActivityInterface activityInterface = gestureState.getActivityInterface(); mLastGestureState = gestureState; - mCallbacks = new RecentsAnimationCallbacks(activityInterface.allowMinimizeSplitScreen()); + mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx), + activityInterface.allowMinimizeSplitScreen()); mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() { @Override public void onRecentsAnimationStart(RecentsAnimationController controller, diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 37fda73d44..f292f1a6e3 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -405,77 +406,39 @@ public final class TaskViewUtils { } /** Legacy version (until shell transitions are enabled) */ - public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull AnimatorSet anim, + public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull TaskView initialView, @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, - @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing, - @NonNull StateManager stateManager, @NonNull DepthController depthController, - int targetStage) { - PendingAnimation out = new PendingAnimation(RECENTS_LAUNCH_DURATION); - boolean isRunningTask = v.isRunningTask(); - TransformParams params = null; - TaskViewSimulator tvs = null; - RecentsView recentsView = v.getRecentsView(); - if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) { - params = recentsView.getLiveTileParams(); - tvs = recentsView.getLiveTileTaskViewSimulator(); + @NonNull RemoteAnimationTargetCompat[] nonAppTargets, + @NonNull Runnable finishCallback) { + + final int[] splitRoots = new int[2]; + for (int i = 0; i < appTargets.length; ++i) { + final int taskId = appTargets[i].taskInfo != null ? appTargets[i].taskInfo.taskId : -1; + final int mode = appTargets[i].mode; + if (taskId == initialView.getTask().key.id || taskId == v.getTask().key.id) { + if (mode != MODE_OPENING) { + throw new IllegalStateException( + "Expected task to be opening, but it is " + mode); + } + splitRoots[taskId == initialView.getTask().key.id ? 0 : 1] = i; + } } - boolean inLiveTileMode = - ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1; - final RemoteAnimationTargets targets = - new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets, - inLiveTileMode ? MODE_CLOSING : MODE_OPENING); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - if (params == null) { - SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); - targets.addReleaseCheck(applier); - - params = new TransformParams() - .setSyncTransactionApplier(applier) - .setTargetSet(targets); + // This is where we should animate the split roots. For now, though, just make them visible. + for (int i = 0; i < 2; ++i) { + t.show(appTargets[splitRoots[i]].leash.getSurfaceControl()); + t.setAlpha(appTargets[splitRoots[i]].leash.getSurfaceControl(), 1.f); } - Rect crop = new Rect(); - Context context = v.getContext(); - DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile(); - if (tvs == null && targets.apps.length > 0) { - tvs = new TaskViewSimulator(recentsView.getContext(), recentsView.getSizeStrategy()); - tvs.setDp(dp); + // This contains the initial state (before animation), so apply this at the beginning of + // the animation. + t.apply(); - // RecentsView never updates the display rotation until swipe-up so the value may - // be stale. Use the display value instead. - int displayRotation = DisplayController.INSTANCE.get(recentsView.getContext()) - .getInfo().rotation; - tvs.getOrientationState().update(displayRotation, displayRotation); - - tvs.setPreview(targets.apps[targets.apps.length - 1]); - tvs.fullScreenProgress.value = 0; - tvs.recentsViewScale.value = 1; -// tvs.setScroll(startScroll); - - // Fade in the task during the initial 20% of the animation - out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, - clampToProgress(LINEAR, 0, 0.2f)); - } - - TaskViewSimulator topMostSimulator = null; - - if (tvs != null) { - out.setFloat(tvs.fullScreenProgress, - AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR); - out.setFloat(tvs.recentsViewScale, - AnimatedFloat.VALUE, tvs.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR); - out.setFloat(tvs.recentsViewScroll, - AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR); - - TaskViewSimulator finalTsv = tvs; - TransformParams finalParams = params; - out.addOnFrameCallback(() -> finalTsv.apply(finalParams)); - topMostSimulator = tvs; - } - - anim.play(out.buildAnim()); + // Once there is an animation, this should be called AFTER the animation completes. + finishCallback.run(); } public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, @@ -490,6 +453,10 @@ public final class TaskViewUtils { PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, nonAppTargets, depthController, pa); + if (launcherClosing) { + // TODO(b/182592057): differentiate between "restore split" vs "launch fullscreen app" + TaskViewUtils.setDividerBarShown(nonAppTargets, true); + } Animator childStateAnimation = null; // Found a visible recents task that matches the opening app, lets launch the app from there @@ -542,4 +509,19 @@ public final class TaskViewUtils { stateManager.setCurrentAnimation(anim, childStateAnimation); anim.addListener(windowAnimEndListener); } + + static void setDividerBarShown(RemoteAnimationTargetCompat[] nonApps, boolean shown) { + // TODO(b/182592057): make this part of the animations instead. + if (nonApps != null && nonApps.length > 0) { + for (int i = 0; i < nonApps.length; ++i) { + final RemoteAnimationTargetCompat targ = nonApps[i]; + if (targ.windowType == TYPE_DOCK_DIVIDER) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setVisibility(targ.leash.getSurfaceControl(), shown); + t.apply(); + t.close(); + } + } + } + } } diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index a147b6808c..2351a4e164 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -22,33 +22,26 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; -import android.animation.AnimatorSet; -import android.app.ActivityOptions; +import android.app.ActivityThread; import android.content.res.Resources; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; -import android.util.Pair; import android.view.Gravity; +import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.Nullable; -import com.android.launcher3.BaseActivity; -import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InsettableFrameLayout; -import com.android.launcher3.LauncherAnimationRunner; -import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory; import com.android.launcher3.R; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskAnimationManager; import com.android.quickstep.TaskViewUtils; import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -92,37 +85,27 @@ public class SplitSelectStateController { ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id} : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id}; - RemoteSplitLaunchAnimationRunner animationRunner = - new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView); + RemoteSplitLaunchTransitionRunner animationRunner = + new RemoteSplitLaunchTransitionRunner(mInitialTaskView, taskView); mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR)); - return; + } else { + // Assume initial task is for top/left part of screen + final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT + ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id} + : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id}; + + RemoteSplitLaunchAnimationRunner animationRunner = + new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner), + 300, 150, + ActivityThread.currentActivityThread().getApplicationThread()); + + mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], null /* mainOptions */, + taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, adapter); } - // Assume initial mInitialTaskId is for top/left part of screen - RemoteAnimationFactory initialSplitRunnerWrapped = new SplitLaunchAnimationRunner( - mInitialTaskView, 0); - RemoteAnimationFactory secondarySplitRunnerWrapped = new SplitLaunchAnimationRunner( - taskView, 1); - RemoteAnimationRunnerCompat initialSplitRunner = new LauncherAnimationRunner( - new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped, - true /* startAtFrontOfQueue */); - RemoteAnimationRunnerCompat secondarySplitRunner = new LauncherAnimationRunner( - new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped, - true /* startAtFrontOfQueue */); - ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation( - new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150)); - ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation( - new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150)); - mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType, - mInitialPosition.mStagePosition, - /*null*/ initialOptions.toBundle()); - Pair compliment = getComplimentaryStageAndPosition(mInitialPosition); - mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first, - compliment.second, - /*null*/ secondaryOptions.toBundle()); - // After successful launch, call resetState - resetState(); } /** @@ -153,12 +136,12 @@ public class SplitSelectStateController { /** * Requires Shell Transitions */ - private class RemoteSplitLaunchAnimationRunner implements RemoteTransitionRunner { + private class RemoteSplitLaunchTransitionRunner implements RemoteTransitionRunner { private final TaskView mInitialTaskView; private final TaskView mTaskView; - RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) { + RemoteSplitLaunchTransitionRunner(TaskView initialTaskView, TaskView taskView) { mInitialTaskView = initialTaskView; mTaskView = taskView; } @@ -173,50 +156,36 @@ public class SplitSelectStateController { } } - /** - * LEGACY - * @return the opposite stage and position from the {@param position} provided as first and - * second object, respectively - * Ex. If position is has stage = Main and position = Top/Left, this will return - * Pair(stage=Side, position=Bottom/Left) - */ - private Pair getComplimentaryStageAndPosition(SplitPositionOption position) { - // Right now this is as simple as flipping between 0 and 1 - int complimentStageType = position.mStageType ^ 1; - int complimentStagePosition = position.mStagePosition ^ 1; - return new Pair<>(complimentStageType, complimentStagePosition); - } - /** * LEGACY * Remote animation runner for animation to launch an app. */ - private class SplitLaunchAnimationRunner implements RemoteAnimationFactory { + private class RemoteSplitLaunchAnimationRunner implements RemoteAnimationRunnerCompat { - private final TaskView mV; - private final int mTargetState; + private final TaskView mInitialTaskView; + private final TaskView mTaskView; - SplitLaunchAnimationRunner(TaskView v, int targetState) { - mV = v; - mTargetState = targetState; + RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) { + mInitialTaskView = initialTaskView; + mTaskView = taskView; } @Override - public void onCreateAnimation(int transit, - RemoteAnimationTargetCompat[] appTargets, - RemoteAnimationTargetCompat[] wallpaperTargets, - RemoteAnimationTargetCompat[] nonAppTargets, - LauncherAnimationRunner.AnimationResult result) { - AnimatorSet anim = new AnimatorSet(); - BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext()); - TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(anim, mV, - appTargets, wallpaperTargets, nonAppTargets, true, activity.getStateManager(), - activity.getDepthController(), mTargetState); - result.setAnimation(anim, activity); + public void onAnimationStart(int transit, RemoteAnimationTargetCompat[] apps, + RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps, + Runnable finishedCallback) { + TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(mInitialTaskView, mTaskView, apps, + wallpapers, nonApps, finishedCallback); + // After successful launch, call resetState + resetState(); + } + + @Override + public void onAnimationCancelled() { + resetState(); } } - /** * To be called if split select was cancelled */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 0103acf92a..897dfbec6d 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -82,6 +82,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Log; import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -128,8 +129,8 @@ import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.ResourceBasedOverride.Overrides; -import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.RunnableList; +import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TranslateEdgeEffect; @@ -988,6 +989,7 @@ public abstract class RecentsView 0) { setSwipeDownShouldLaunchApp(true); @@ -1159,6 +1164,7 @@ public abstract class RecentsView 0) { mFocusedTaskId = getTaskViewAt(0).getTaskId(); + Log.d("b/193125090", "applyLoadPlan - mFocusedTaskId: " + mFocusedTaskId); } updateTaskSize(); @@ -1475,14 +1481,22 @@ public abstract class RecentsView - + diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml index 1aeda50c5e..6fd4a8580c 100644 --- a/res/layout/add_item_confirmation_activity.xml +++ b/res/layout/add_item_confirmation_activity.xml @@ -65,12 +65,19 @@ android:alpha="0.7" android:importantForAccessibility="no"/> - + android:layout_marginVertical="16dp" + android:layout_weight="1"> + + + 56dp - 24dp + 28dp 24dp 32dp 16dp 20dp 16sp 16dp + 28dp 48dp diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java index 1cc7f53bf9..b92c476e5a 100644 --- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java @@ -18,6 +18,7 @@ package com.android.launcher3.widget; import static com.android.launcher3.Utilities.ATLEAST_R; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.widget.BaseWidgetSheet.MAX_WIDTH_SCALE_FOR_LARGER_SCREEN; import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; @@ -25,10 +26,12 @@ import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.WindowInsets; +import android.widget.ScrollView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; @@ -44,6 +47,7 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView 0) { + mNoIntercept = true; + } + } + return super.onControllerInterceptTouchEvent(ev); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int width = r - l; @@ -93,6 +110,15 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView implements OnClickListener, OnLongClickListener, DragSource, PopupDataProvider.PopupDataChangeListener, Insettable { + /** The default number of cells that can fit horizontally in a widget sheet. */ + protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4; /** * The maximum scale, [0, 1], of the device screen width that the widgets picker can consume * on large screen devices. */ - protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.8f; + protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.89f; protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN = "launcher.widgets_education_tip_seen"; @@ -152,6 +154,17 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView MeasureSpec.getSize(heightMeasureSpec)); } + /** Returns the number of cells that can fit horizontally in a given {@code content}. */ + protected int computeMaxHorizontalSpans(View content, int contentHorizontalPaddingPx) { + DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); + int availableWidth = content.getMeasuredWidth() - contentHorizontalPaddingPx; + Point cellSize = deviceProfile.getCellSize(); + if (cellSize.x > 0) { + return availableWidth / cellSize.x; + } + return DEFAULT_MAX_HORIZONTAL_SPANS; + } + private boolean beginDraggingWidget(WidgetCell v) { // Get the widget preview as the drag representation WidgetImageView image = v.getWidgetView(); diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index c3edcce95a..6605509c89 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -177,6 +177,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mWidgetImageContainer.removeView(mAppWidgetHostViewPreview); } mAppWidgetHostViewPreview = null; + mItem = null; } public void setSourceContainer(int sourceContainer) { diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index c045cf1b43..14aeaf6048 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -70,9 +70,11 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { private static final int DEFAULT_CLOSE_DURATION = 200; private static final long EDUCATION_TIP_DELAY_MS = 300; + private final int mWidgetSheetContentHorizontalPadding; + private ItemInfo mOriginalItemInfo; private final int mMaxTableHeight; - private int mMaxHorizontalSpan = 4; + private int mMaxHorizontalSpan = DEFAULT_MAX_HORIZONTAL_SPANS; private final OnLayoutChangeListener mLayoutChangeListenerToShowTips = new OnLayoutChangeListener() { @@ -117,6 +119,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { if (!hasSeenEducationTip()) { addOnLayoutChangeListener(mLayoutChangeListenerToShowTips); } + + mWidgetSheetContentHorizontalPadding = getResources().getDimensionPixelSize( + R.dimen.widget_list_horizontal_margin); } @Override @@ -137,10 +142,8 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { private boolean updateMaxSpansPerRow() { if (getMeasuredWidth() == 0) return false; - int paddingPx = 2 * getResources().getDimensionPixelOffset( - R.dimen.widget_cell_horizontal_padding); - int maxHorizontalSpan = findViewById(R.id.widgets_table).getMeasuredWidth() - / (mActivityContext.getDeviceProfile().cellWidthPx + paddingPx); + int maxHorizontalSpan = computeMaxHorizontalSpans(mContent, + mWidgetSheetContentHorizontalPadding); if (mMaxHorizontalSpan != maxHorizontalSpan) { // Ensure the table layout is showing widgets in the right column after measure. mMaxHorizontalSpan = maxHorizontalSpan; diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 6c2cca6024..5e1a534bb0 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -25,6 +25,7 @@ import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.pm.LauncherApps; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; import android.os.Process; import android.os.UserHandle; @@ -148,13 +149,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet private final int mTabsHeight; private final int mViewPagerTopPadding; private final int mSearchAndRecommendationContainerBottomMargin; - private final int mWidgetCellHorizontalPadding; + private final int mWidgetSheetContentHorizontalPadding; @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView; @Nullable private PersonalWorkPagedView mViewPager; private boolean mIsInSearchMode; private boolean mIsNoWidgetsViewNeeded; - private int mMaxSpansPerRow = 4; + private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS; private View mTabsView; private TextView mNoWidgetsView; private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder; @@ -166,19 +167,20 @@ public class WidgetsFullSheet extends BaseWidgetSheet mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY)); mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK)); mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH)); + + Resources resources = getResources(); mTabsHeight = mHasWorkProfile - ? getContext().getResources() - .getDimensionPixelSize(R.dimen.all_apps_header_pill_height) + ? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height) : 0; mViewPagerTopPadding = mHasWorkProfile ? getContext().getResources() .getDimensionPixelSize(R.dimen.widget_picker_view_pager_top_padding) : 0; - mSearchAndRecommendationContainerBottomMargin = getContext().getResources() - .getDimensionPixelSize(mHasWorkProfile + mSearchAndRecommendationContainerBottomMargin = resources.getDimensionPixelSize( + mHasWorkProfile ? R.dimen.search_and_recommended_widgets_container_small_bottom_margin : R.dimen.search_and_recommended_widgets_container_bottom_margin); - mWidgetCellHorizontalPadding = 2 * getResources().getDimensionPixelOffset( + mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize( R.dimen.widget_cell_horizontal_padding); } @@ -375,11 +377,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet private boolean updateMaxSpansPerRow() { if (getMeasuredWidth() == 0) return false; - int previousMaxSpansPerRow = mMaxSpansPerRow; - mMaxSpansPerRow = getMeasuredWidth() - / (mActivityContext.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding); - - if (previousMaxSpansPerRow != mMaxSpansPerRow) { + int maxHorizontalSpans = computeMaxHorizontalSpans(mContent, + mWidgetSheetContentHorizontalPadding); + if (mMaxSpansPerRow != maxHorizontalSpans) { + mMaxSpansPerRow = maxHorizontalSpans; mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow( mMaxSpansPerRow); mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(