diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 4a26494b6d..8b28597dd1 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -47,7 +47,8 @@ + android:exported="true" + android:excludeFromRecents="true" /> mOnInitListener; + + public LauncherInitListener(BiPredicate onInitListener) { + mOnInitListener = onInitListener; + } + + @Override + protected boolean init(Launcher launcher, boolean alreadyOnHome) { + // For the duration of the gesture, lock the screen orientation to ensure that we do not + // rotate mid-quickscrub + launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK); + return mOnInitListener.test(launcher, alreadyOnHome); + } + + @Override + public void register() { + initWhenReady(); + } + + @Override + public void unregister() { + clearReference(); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 789185a395..637ce60a28 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -18,6 +18,7 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; +import android.content.Context; import android.view.View; import android.view.View.AccessibilityDelegate; @@ -73,7 +74,8 @@ public class UiFactory { } } } - OverviewInteractionState.setBackButtonVisible(launcher, shouldBackButtonBeVisible); + OverviewInteractionState.getInstance(launcher) + .setBackButtonVisible(shouldBackButtonBeVisible); } public static void resetOverview(Launcher launcher) { @@ -81,15 +83,15 @@ public class UiFactory { recents.reset(); } - public static void onStart(Launcher launcher) { - RecentsModel model = RecentsModel.getInstance(launcher); + public static void onStart(Context context) { + RecentsModel model = RecentsModel.getInstance(context); if (model != null) { model.onStart(); } } - public static void onTrimMemory(Launcher launcher, int level) { - RecentsModel model = RecentsModel.getInstance(launcher); + public static void onTrimMemory(Context context, int level) { + RecentsModel model = RecentsModel.getInstance(context); if (model != null) { model.onTrimMemory(level); } diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java new file mode 100644 index 0000000000..a9da4f916f --- /dev/null +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2018 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; + +import static com.android.launcher3.LauncherState.FAST_OVERVIEW; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; +import static com.android.launcher3.anim.Interpolators.LINEAR; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.ActivityOptions; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.view.View; + +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherInitListener; +import com.android.launcher3.LauncherState; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.quickstep.views.LauncherLayoutListener; +import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.AssistDataReceiver; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; + +import java.util.function.BiPredicate; + +/** + * Utility class which abstracts out the logical differences between Launcher and RecentsActivity. + */ +public interface ActivityControlHelper { + + LayoutListener createLayoutListener(T activity); + + void onQuickstepGestureStarted(T activity, boolean activityVisible); + + void onQuickInteractionStart(T activity, boolean activityVisible); + + void executeOnNextDraw(T activity, TaskView targetView, Runnable action); + + void onTransitionCancelled(T activity, boolean activityVisible); + + int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect); + + void onSwipeUpComplete(T activity); + + void prepareRecentsUI(T activity, boolean activityVisible); + + AnimatorPlaybackController createControllerForVisibleActivity(T activity); + + AnimatorPlaybackController createControllerForHiddenActivity(T activity, int transitionLength); + + ActivityInitListener createActivityInitListener(BiPredicate onInitListener); + + void startRecents(Context context, Intent intent, AssistDataReceiver assistDataReceiver, + RecentsAnimationListener remoteAnimationListener); + + class LauncherActivityControllerHelper implements ActivityControlHelper { + + @Override + public LayoutListener createLayoutListener(Launcher activity) { + return new LauncherLayoutListener(activity); + } + + @Override + public void onQuickstepGestureStarted(Launcher activity, boolean activityVisible) { + activity.onQuickstepGestureStarted(activityVisible); + } + + @Override + public void onQuickInteractionStart(Launcher activity, boolean activityVisible) { + activity.getStateManager().goToState(FAST_OVERVIEW, activityVisible); + } + + @Override + public void executeOnNextDraw(Launcher activity, TaskView targetView, Runnable action) { + ViewOnDrawExecutor executor = new ViewOnDrawExecutor() { + @Override + public void onViewDetachedFromWindow(View v) { + if (!isCompleted()) { + runAllTasks(); + } + } + }; + executor.attachTo(activity, targetView, false /* waitForLoadAnimation */); + executor.execute(action); + } + + @Override + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { + RecentsView.getPageRect(dp, context, outRect); + if (dp.isVerticalBarLayout()) { + Rect targetInsets = dp.getInsets(); + int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; + return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; + } else { + return dp.heightPx - outRect.bottom; + } + } + + @Override + public void onTransitionCancelled(Launcher activity, boolean activityVisible) { + LauncherState startState = activity.getStateManager().getRestState(); + activity.getStateManager().goToState(startState, activityVisible); + } + + @Override + public void onSwipeUpComplete(Launcher activity) { + // Re apply state in case we did something funky during the transition. + activity.getStateManager().reapplyState(); + } + + @Override + public void prepareRecentsUI(Launcher activity, boolean activityVisible) { + LauncherState startState = activity.getStateManager().getState(); + if (startState.disableRestore) { + startState = activity.getStateManager().getRestState(); + } + activity.getStateManager().setRestState(startState); + + if (!activityVisible) { + activity.getStateManager().goToState(OVERVIEW, false); + + // Optimization, hide the all apps view to prevent layout while initializing + activity.getAppsView().setVisibility(View.GONE); + } + } + + @Override + public AnimatorPlaybackController createControllerForVisibleActivity(Launcher activity) { + DeviceProfile dp = activity.getDeviceProfile(); + long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); + return activity.getStateManager().createAnimationToNewWorkspace(OVERVIEW, accuracy); + } + + @Override + public AnimatorPlaybackController createControllerForHiddenActivity( + Launcher activity, int transitionLength) { + float startProgress; + AllAppsTransitionController controller = activity.getAllAppsController(); + + if (activity.getDeviceProfile().isVerticalBarLayout()) { + startProgress = 1; + } else { + float scrollRange = Math.max(controller.getShiftRange(), 1); + startProgress = (transitionLength / scrollRange) + 1; + } + AnimatorSet anim = new AnimatorSet(); + ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, + startProgress, OVERVIEW.getVerticalProgress(activity)); + shiftAnim.setInterpolator(LINEAR); + anim.play(shiftAnim); + + // TODO: Link this animation to state animation, so that it is cancelled + // automatically on state change + anim.setDuration(transitionLength * 2); + return AnimatorPlaybackController.wrap(anim, transitionLength * 2); + } + + @Override + public ActivityInitListener createActivityInitListener( + BiPredicate onInitListener) { + return new LauncherInitListener(onInitListener); + } + + @Override + public void startRecents(Context context, Intent intent, + AssistDataReceiver assistDataReceiver, + RecentsAnimationListener remoteAnimationListener) { + ActivityManagerWrapper.getInstance().startRecentsActivity( + intent, assistDataReceiver, remoteAnimationListener, null, null); + } + } + + class FallbackActivityControllerHelper implements ActivityControlHelper { + + @Override + public void onQuickstepGestureStarted(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public void onQuickInteractionStart(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public void executeOnNextDraw(RecentsActivity activity, TaskView targetView, + Runnable action) { + // TODO: + new Handler(Looper.getMainLooper()).post(action); + } + + @Override + public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { + FallbackRecentsView.getCenterPageRect(dp, context, outRect); + if (dp.isVerticalBarLayout()) { + Rect targetInsets = dp.getInsets(); + int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; + return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; + } else { + return dp.heightPx - outRect.bottom; + } + } + + @Override + public void onSwipeUpComplete(RecentsActivity activity) { + // TODO: + } + + @Override + public void prepareRecentsUI(RecentsActivity activity, boolean activityVisible) { + // TODO: + } + + @Override + public AnimatorPlaybackController createControllerForVisibleActivity( + RecentsActivity activity) { + DeviceProfile dp = activity.getDeviceProfile(); + return createControllerForHiddenActivity(activity, Math.max(dp.widthPx, dp.heightPx)); + } + + @Override + public AnimatorPlaybackController createControllerForHiddenActivity( + RecentsActivity activity, int transitionLength) { + // We do not animate anything. Create a empty controller + AnimatorSet anim = new AnimatorSet(); + return AnimatorPlaybackController.wrap(anim, transitionLength * 2); + } + + @Override + public LayoutListener createLayoutListener(RecentsActivity activity) { + // We do not change anything as part of layout changes in fallback activity. Return a + // default layout listener. + return new LayoutListener() { + @Override + public void open() { } + + @Override + public void setHandler(WindowTransformSwipeHandler handler) { } + + @Override + public void finish() { } + }; + } + + @Override + public ActivityInitListener createActivityInitListener( + BiPredicate onInitListener) { + return new RecentsActivityTracker(onInitListener); + } + + @Override + public void startRecents(Context context, Intent intent, + AssistDataReceiver assistDataReceiver, + final RecentsAnimationListener remoteAnimationListener) { + ActivityOptions options = + ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( + new FallbackActivityOptions(remoteAnimationListener), 10000, 10000)); + context.startActivity(intent, options.toBundle()); + } + } + + interface LayoutListener { + + void open(); + + void setHandler(WindowTransformSwipeHandler handler); + + void finish(); + } + + interface ActivityInitListener { + + void register(); + + void unregister(); + } +} diff --git a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java new file mode 100644 index 0000000000..3a7fb2db6d --- /dev/null +++ b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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; + +import android.graphics.Rect; +import android.util.Log; + +import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.RecentsAnimationControllerCompat; +import com.android.systemui.shared.system.RecentsAnimationListener; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.WindowManagerWrapper; + +/** + * Temporary class to create activity options to emulate recents transition for fallback activtiy. + */ +public class FallbackActivityOptions implements RemoteAnimationRunnerCompat { + + private final RecentsAnimationListener mListener; + + public FallbackActivityOptions(RecentsAnimationListener listener) { + mListener = listener; + } + + @Override + public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, + Runnable runnable) { + DummyRecentsAnimationControllerCompat dummyRecentsAnim = + new DummyRecentsAnimationControllerCompat(runnable); + + Rect insets = new Rect(); + WindowManagerWrapper.getInstance().getStableInsets(insets); + mListener.onAnimationStart(dummyRecentsAnim, targetCompats, insets, null); + } + + @Override + public void onAnimationCancelled() { + mListener.onAnimationCanceled(); + } + + private static class DummyRecentsAnimationControllerCompat + extends RecentsAnimationControllerCompat { + + final Runnable mFinishCallback; + + public DummyRecentsAnimationControllerCompat(Runnable finishCallback) { + mFinishCallback = finishCallback; + } + + @Override + public ThumbnailData screenshotTask(int taskId) { + return new ThumbnailData(); + } + + @Override + public void setInputConsumerEnabled(boolean enabled) { } + + @Override + public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { } + + @Override + public void finish(boolean toHome) { + if (toHome) { + mFinishCallback.run(); + } + } + } +} diff --git a/quickstep/src/com/android/quickstep/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/FallbackRecentsView.java index 22f6e0cc25..032d753a68 100644 --- a/quickstep/src/com/android/quickstep/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/FallbackRecentsView.java @@ -48,10 +48,21 @@ public class FallbackRecentsView extends RecentsView implements setPadding(padding.left, padding.top, padding.right, padding.bottom); } - public static void verticalCenter(Rect padding, DeviceProfile dp) { + private static void verticalCenter(Rect padding, DeviceProfile dp) { Rect insets = dp.getInsets(); int totalSpace = (padding.top + padding.bottom - insets.top - insets.bottom) / 2; padding.top = insets.top + totalSpace; padding.bottom = insets.bottom + totalSpace; } + + public static void getCenterPageRect(DeviceProfile grid, Context context, Rect outRect) { + Rect targetPadding = getPadding(grid, context); + verticalCenter(targetPadding, grid); + Rect insets = grid.getInsets(); + outRect.set( + targetPadding.left + insets.left, + targetPadding.top + insets.top, + grid.widthPx - targetPadding.right - insets.right, + grid.heightPx - targetPadding.bottom - insets.bottom); + } } diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 69e7933b9d..ab19c6e774 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -22,7 +22,6 @@ import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; @@ -36,6 +35,7 @@ import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Looper; +import android.util.Log; import android.view.Choreographer; import android.view.Display; import android.view.MotionEvent; @@ -46,7 +46,6 @@ import android.view.WindowManager; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.TraceHelper; -import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.AssistDataReceiver; import com.android.systemui.shared.system.BackgroundExecutor; @@ -67,13 +66,13 @@ import java.util.concurrent.TimeUnit; public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer { private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150; - private static final int[] DEFERRED_HIT_TARGETS = DEBUG_SHOW_OVERVIEW_BUTTON + private static final int[] DEFERRED_HIT_TARGETS = false ? new int[] {HIT_TARGET_BACK, HIT_TARGET_OVERVIEW} : new int[] {HIT_TARGET_BACK}; private final RunningTaskInfo mRunningTask; private final RecentsModel mRecentsModel; private final Intent mHomeIntent; - private final ISystemUiProxy mISystemUiProxy; + private final ActivityControlHelper mActivityControlHelper; private final MainThreadExecutor mMainThreadExecutor; private final Choreographer mBackgroundThreadChoreographer; @@ -93,7 +92,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private boolean mIsGoingToHome; public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo, - RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy, + RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl, MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer, @HitTarget int downHitTarget, VelocityTracker velocityTracker) { super(base); @@ -101,7 +100,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC mRecentsModel = recentsModel; mHomeIntent = homeIntent; mVelocityTracker = velocityTracker; - mISystemUiProxy = systemUiProxy; + mActivityControlHelper = activityControl; mMainThreadExecutor = mainThreadExecutor; mBackgroundThreadChoreographer = backgroundThreadChoreographer; mIsDeferredDownTarget = Arrays.binarySearch(DEFERRED_HIT_TARGETS, downHitTarget) >= 0; @@ -205,8 +204,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC private void startTouchTrackingForWindowAnimation(long touchTimeMs) { // Create the shared handler - final WindowTransformSwipeHandler handler = - new WindowTransformSwipeHandler(mRunningTask, this, touchTimeMs); + final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler( + mRunningTask, this, touchTimeMs, mActivityControlHelper); // Preload the plan mRecentsModel.loadTasks(mRunningTask.id, null); @@ -223,8 +222,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC handler.initWhenReady(); TraceHelper.beginSection("RecentsController"); - Runnable startActivity = () -> ActivityManagerWrapper.getInstance() - .startRecentsActivity(mHomeIntent, + Runnable startActivity = () -> mActivityControlHelper.startRecents(this, mHomeIntent, new AssistDataReceiver() { @Override public void onHandleAssistData(Bundle bundle) { @@ -253,7 +251,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC handler.onRecentsAnimationCanceled(); } } - }, null, null); + }); if (Looper.myLooper() != Looper.getMainLooper()) { startActivity.run(); diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index ef58fad29b..38c25a341b 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -16,7 +16,6 @@ package com.android.quickstep; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; import android.annotation.TargetApi; import android.app.ActivityManager.RecentTaskInfo; @@ -33,6 +32,8 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.states.InternalStateHandler; +import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper; +import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -42,7 +43,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; @TargetApi(Build.VERSION_CODES.P) public class OverviewCommandHelper extends InternalStateHandler { - private static final boolean DEBUG_START_FALLBACK_ACTIVITY = DEBUG_SHOW_OVERVIEW_BUTTON; + private static final boolean DEBUG_START_FALLBACK_ACTIVITY = false; private final Context mContext; private final ActivityManagerWrapper mAM; @@ -61,7 +62,15 @@ public class OverviewCommandHelper extends InternalStateHandler { .setPackage(context.getPackageName()) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent, 0); - launcher = new ComponentName(context.getPackageName(), info.activityInfo.name); + + if (DEBUG_START_FALLBACK_ACTIVITY) { + launcher = new ComponentName(context, RecentsActivity.class); + homeIntent.addCategory(Intent.CATEGORY_DEFAULT) + .removeCategory(Intent.CATEGORY_HOME); + } else { + launcher = new ComponentName(context.getPackageName(), info.activityInfo.name); + } + // Clear the packageName as system can fail to dedupe it b/64108432 homeIntent.setComponent(launcher).setPackage(null); } @@ -74,7 +83,7 @@ public class OverviewCommandHelper extends InternalStateHandler { public void onOverviewToggle() { getLauncher().runOnUiThread(() -> { - if (DEBUG_START_FALLBACK_ACTIVITY) { + if (isUsingFallbackActivity()) { mContext.startActivity(new Intent(mContext, RecentsActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent .FLAG_ACTIVITY_CLEAR_TASK)); @@ -147,4 +156,15 @@ public class OverviewCommandHelper extends InternalStateHandler { return false; } + public boolean isUsingFallbackActivity() { + return DEBUG_START_FALLBACK_ACTIVITY; + } + + public ActivityControlHelper getActivityControlHelper() { + if (DEBUG_START_FALLBACK_ACTIVITY) { + return new FallbackActivityControllerHelper(); + } else { + return new LauncherActivityControllerHelper(); + } + } } diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java index 4af89bf46b..522a88369f 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -15,20 +15,28 @@ */ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.DEBUG_SHOW_OVERVIEW_BUTTON; +import static com.android.launcher3.Utilities.getPrefs; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.support.annotation.WorkerThread; import android.util.Log; +import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.ISystemUiProxy; +import java.util.concurrent.ExecutionException; + /** * Sets overview interaction flags, such as: * @@ -39,55 +47,109 @@ import com.android.systemui.shared.recents.ISystemUiProxy; * * @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags. */ -public class OverviewInteractionState { +public class OverviewInteractionState implements OnSharedPreferenceChangeListener { private static final String TAG = "OverviewFlags"; - private static final Handler sUiHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - updateOverviewInteractionFlag((Context) msg.obj, msg.what, msg.arg1 == 1); - } - }; - private static final Handler sBackgroundHandler = new Handler( - UiThreadHelper.getBackgroundLooper()) { - @Override - public void handleMessage(Message msg) { - ISystemUiProxy systemUiProxy = (ISystemUiProxy) msg.obj; - int flags = msg.what; - try { - systemUiProxy.setInteractionState(flags); - } catch (RemoteException e) { - Log.w(TAG, "Unable to update overview interaction flags", e); + + // We do not need any synchronization for this variable as its only written on UI thread. + private static OverviewInteractionState INSTANCE; + + public static OverviewInteractionState getInstance(final Context context) { + if (INSTANCE == null) { + if (Looper.myLooper() == Looper.getMainLooper()) { + INSTANCE = new OverviewInteractionState(context.getApplicationContext()); + } else { + try { + return new MainThreadExecutor().submit( + () -> OverviewInteractionState.getInstance(context)).get(); + } catch (InterruptedException|ExecutionException e) { + throw new RuntimeException(e); + } } } - }; - - private static int sFlags = DEBUG_SHOW_OVERVIEW_BUTTON ? FLAG_SHOW_OVERVIEW_BUTTON : 0; - - public static void setBackButtonVisible(Context context, boolean visible) { - updateFlagOnUi(context, FLAG_HIDE_BACK_BUTTON, !visible); + return INSTANCE; } - private static void updateFlagOnUi(Context context, int flag, boolean enabled) { - sUiHandler.removeMessages(flag); - sUiHandler.sendMessage(sUiHandler.obtainMessage(flag, enabled ? 1 : 0, 0, context)); + private static final String KEY_SWIPE_UP_ENABLED = "pref_enable_quickstep"; + + private static final int MSG_SET_PROXY = 200; + private static final int MSG_SET_BACK_BUTTON_VISIBLE = 201; + private static final int MSG_SET_SWIPE_UP_ENABLED = 202; + + private final Handler mUiHandler; + private final Handler mBgHandler; + + // These are updated on the background thread + private ISystemUiProxy mISystemUiProxy; + private boolean mBackButtonVisible = true; + private boolean mSwipeUpEnabled = true; + + private OverviewInteractionState(Context context) { + mUiHandler = new Handler(this::handleUiMessage); + mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage); + + SharedPreferences prefs = getPrefs(context); + prefs.registerOnSharedPreferenceChangeListener(this); + onSharedPreferenceChanged(prefs, KEY_SWIPE_UP_ENABLED); } - private static void updateOverviewInteractionFlag(Context context, int flag, boolean enabled) { - if (enabled) { - sFlags |= flag; - } else { - sFlags &= ~flag; + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String s) { + if (KEY_SWIPE_UP_ENABLED.equals(s)) { + mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED); + boolean swipeUpEnabled = prefs.getBoolean(s, true); + mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, + swipeUpEnabled ? 1 : 0, 0).sendToTarget(); } + } - ISystemUiProxy systemUiProxy = RecentsModel.getInstance(context).getSystemUiProxy(); - if (systemUiProxy == null) { - Log.w(TAG, "Unable to update overview interaction flags; not bound to service"); + public void setBackButtonVisible(boolean visible) { + mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_VISIBLE); + mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_VISIBLE, visible ? 1 : 0, 0) + .sendToTarget(); + } + + public void setSystemUiProxy(ISystemUiProxy proxy) { + mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget(); + } + + private boolean handleUiMessage(Message msg) { + mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2).sendToTarget(); + return true; + } + + private boolean handleBgMessage(Message msg) { + switch (msg.what) { + case MSG_SET_PROXY: + mISystemUiProxy = (ISystemUiProxy) msg.obj; + break; + case MSG_SET_BACK_BUTTON_VISIBLE: + mBackButtonVisible = msg.arg1 != 0; + break; + case MSG_SET_SWIPE_UP_ENABLED: + mSwipeUpEnabled = msg.arg1 != 0; + break; + } + applyFlags(); + return true; + } + + @WorkerThread + private void applyFlags() { + if (mISystemUiProxy == null) { return; } - // If we aren't already setting these flags, do so now on the background thread. - if (!sBackgroundHandler.hasMessages(sFlags)) { - sBackgroundHandler.sendMessage(sBackgroundHandler.obtainMessage(sFlags, systemUiProxy)); + + int flags; + if (mSwipeUpEnabled) { + flags = mBackButtonVisible ? 0 : FLAG_HIDE_BACK_BUTTON; + } else { + flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON; + } + try { + mISystemUiProxy.setInteractionState(flags); + } catch (RemoteException e) { + Log.w(TAG, "Unable to update overview interaction flags", e); } } } diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 1d443fd2af..12e1a2bd8e 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -25,6 +25,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.badge.BadgeInfo; +import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.views.BaseDragLayer; /** @@ -49,6 +50,8 @@ public class RecentsActivity extends BaseDraggingActivity { setContentView(R.layout.fallback_recents_activity); mRecentsRootView = findViewById(R.id.drag_layer); mFallbackRecentsView = findViewById(R.id.overview_panel); + + RecentsActivityTracker.onRecentsActivityCreate(this); } @Override @@ -56,6 +59,11 @@ public class RecentsActivity extends BaseDraggingActivity { return mRecentsRootView; } + @Override + public View getRootView() { + return mRecentsRootView; + } + @Override public T getOverviewPanel() { return (T) mFallbackRecentsView; @@ -73,4 +81,16 @@ public class RecentsActivity extends BaseDraggingActivity { @Override public void invalidateParent(ItemInfo info) { } + + @Override + protected void onStart() { + super.onStart(); + UiFactory.onStart(this); + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + UiFactory.onTrimMemory(this, level); + } } diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java new file mode 100644 index 0000000000..6a82dc0d00 --- /dev/null +++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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; + +import android.annotation.TargetApi; +import android.os.Build; + +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; + +import java.lang.ref.WeakReference; +import java.util.function.BiPredicate; + +/** + * Utility class to track create/destroy for RecentsActivity + */ +@TargetApi(Build.VERSION_CODES.P) +public class RecentsActivityTracker implements ActivityInitListener { + + private static final Object LOCK = new Object(); + private static WeakReference sTracker = new WeakReference<>(null); + + private final BiPredicate mOnInitListener; + + public RecentsActivityTracker(BiPredicate onInitListener) { + mOnInitListener = onInitListener; + } + + @Override + public void register() { + synchronized (LOCK) { + sTracker = new WeakReference<>(this); + } + } + + @Override + public void unregister() { + synchronized (LOCK) { + if (sTracker.get() == this) { + sTracker.clear(); + } + } + } + + public static void onRecentsActivityCreate(RecentsActivity activity) { + synchronized (LOCK) { + RecentsActivityTracker tracker = sTracker.get(); + if (tracker != null && tracker.mOnInitListener.test(activity, false)) { + sTracker.clear(); + } + } + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index ecd6c26493..df7214e2f4 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -47,7 +47,6 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; -import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.IOverviewProxy; @@ -61,7 +60,6 @@ import com.android.systemui.shared.system.NavigationBarCompat.HitTarget; @TargetApi(Build.VERSION_CODES.O) public class TouchInteractionService extends Service { - public static final boolean DEBUG_SHOW_OVERVIEW_BUTTON = false; public static final boolean DEBUG_OPEN_OVERVIEW_VIA_ALT_TAB = false; private static final SparseArray sMotionEventNames; @@ -107,9 +105,7 @@ public class TouchInteractionService extends Service { mRecentsModel.setSystemUiProxy(mISystemUiProxy); RemoteRunnable.executeSafely(() -> mISystemUiProxy.setRecentsOnboardingText( getResources().getString(R.string.recents_swipe_up_onboarding))); - Launcher launcher = (Launcher) LauncherAppState.getInstance( - TouchInteractionService.this).getModel().getCallback(); - UiFactory.onLauncherStateOrFocusChanged(launcher); + mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy); } @Override @@ -174,6 +170,7 @@ public class TouchInteractionService extends Service { private MainThreadExecutor mMainThreadExecutor; private ISystemUiProxy mISystemUiProxy; private OverviewCommandHelper mOverviewCommandHelper; + private OverviewInteractionState mOverviewInteractionState; private Choreographer mMainThreadChoreographer; private Choreographer mBackgroundThreadChoreographer; @@ -187,6 +184,7 @@ public class TouchInteractionService extends Service { mOverviewCommandHelper = new OverviewCommandHelper(this); mMainThreadChoreographer = Choreographer.getInstance(); mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer); + mOverviewInteractionState = OverviewInteractionState.getInstance(this); sConnected = true; @@ -235,7 +233,8 @@ public class TouchInteractionService extends Service { tracker = VelocityTracker.obtain(); } return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel, - mOverviewCommandHelper.homeIntent, mISystemUiProxy, mMainThreadExecutor, + mOverviewCommandHelper.homeIntent, + mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor, mBackgroundThreadChoreographer, downHitTarget, tracker); } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 060d680fd0..96cd4a0243 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -15,22 +15,16 @@ */ package com.android.quickstep; -import static com.android.launcher3.LauncherState.FAST_OVERVIEW; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; -import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION; import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB; import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH; import static com.android.quickstep.TouchConsumer.isInteractionQuick; -import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously; +import static com.android.systemui.shared.recents.utilities.Utilities + .postAtFrontOfQueueAsynchronously; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.animation.Animator; -import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; @@ -53,24 +47,22 @@ import android.view.View; import android.view.ViewTreeObserver.OnDrawListener; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.states.InternalStateHandler; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.quickstep.ActivityControlHelper.ActivityInitListener; +import com.android.quickstep.ActivityControlHelper.LayoutListener; import com.android.quickstep.TouchConsumer.InteractionType; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -120,7 +112,7 @@ class MetricsLogger { } @TargetApi(Build.VERSION_CODES.O) -public class WindowTransformSwipeHandler extends InternalStateHandler { +public class WindowTransformSwipeHandler { private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName(); private static final boolean DEBUG_STATES = false; @@ -209,12 +201,14 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { private final Context mContext; private final int mRunningTaskId; + private final ActivityControlHelper mActivityControlHelper; + private final ActivityInitListener mActivityInitListener; private MultiStateCallback mStateCallback; private AnimatorPlaybackController mLauncherTransitionController; - private Launcher mLauncher; - private LauncherLayoutListener mLauncherLayoutListener; + private T mActivity; + private LayoutListener mLayoutListener; private RecentsView mRecentsView; private QuickScrubController mQuickScrubController; @@ -237,10 +231,15 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { private long mLauncherFrameDrawnTime; private final MetricsLogger mMetricsLogger = new MetricsLogger(); - WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs) { + WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, + ActivityControlHelper controller) { mContext = context; mRunningTaskId = runningTaskInfo.id; mTouchTimeMs = touchTimeMs; + mActivityControlHelper = controller; + mActivityInitListener = mActivityControlHelper + .createActivityInitListener(this::onActivityInit); + // Register the input consumer on the UI thread, to ensure that it runs after any pending // unregister calls mMainExecutor.execute(mInputConsumer::registerInputConsumer); @@ -313,7 +312,8 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { mSourceStackBounds.height() - mSourceInsets.bottom); Rect tempRect = new Rect(); - RecentsView.getPageRect(dp, mContext, tempRect); + mTransitionDragLength = mActivityControlHelper + .getSwipeUpDestinationAndLength(dp, mContext, tempRect); mTargetRect.set(tempRect); mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left, @@ -334,14 +334,6 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0), Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0)); mSourceRect.set(scaledTargetRect); - - Rect targetInsets = dp.getInsets(); - if (dp.isVerticalBarLayout()) { - int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; - mTransitionDragLength = dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; - } else { - mTransitionDragLength = dp.heightPx - tempRect.bottom; - } } private long getFadeInDuration() { @@ -356,40 +348,39 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } } - @Override - protected boolean init(final Launcher launcher, boolean alreadyOnHome) { - if (launcher == mLauncher) { + public void initWhenReady() { + mActivityInitListener.register(); + } + + private boolean onActivityInit(final T activity, Boolean alreadyOnHome) { + if (mActivity == activity) { return true; } - if (mLauncher != null) { + if (mActivity != null) { // The launcher may have been recreated as a result of device rotation. int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES; initStateCallbacks(); mStateCallback.setState(oldState); - mLauncherLayoutListener.setHandler(null); + mLayoutListener.setHandler(null); } mWasLauncherAlreadyVisible = alreadyOnHome; - mLauncher = launcher; + mActivity = activity; - // For the duration of the gesture, lock the screen orientation to ensure that we do not - // rotate mid-quickscrub - mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK); - - mRecentsView = mLauncher.getOverviewPanel(); + mRecentsView = activity.getOverviewPanel(); mQuickScrubController = mRecentsView.getQuickScrubController(); - mLauncherLayoutListener = new LauncherLayoutListener(mLauncher); + mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity); mStateCallback.setState(STATE_LAUNCHER_PRESENT); if (alreadyOnHome) { - onLauncherStart(launcher); + onLauncherStart(activity); } else { - launcher.setOnStartCallback(this::onLauncherStart); + activity.setOnStartCallback(this::onLauncherStart); } return true; } - private void onLauncherStart(final Launcher launcher) { - if (mLauncher != launcher) { + private void onLauncherStart(final T activity) { + if (mActivity != activity) { return; } if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) { @@ -397,31 +388,21 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } mStateCallback.setState(STATE_LAUNCHER_STARTED); - LauncherState startState = mLauncher.getStateManager().getState(); - if (startState.disableRestore) { - startState = mLauncher.getStateManager().getRestState(); - } - mLauncher.getStateManager().setRestState(startState); - - AbstractFloatingView.closeAllOpenViews(mLauncher, mWasLauncherAlreadyVisible); + mActivityControlHelper.prepareRecentsUI(mActivity, mWasLauncherAlreadyVisible); + AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible); - if (mWasLauncherAlreadyVisible && !mLauncher.getAppTransitionManager().isAnimating()) { - DeviceProfile dp = mLauncher.getDeviceProfile(); - long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); - mLauncherTransitionController = mLauncher.getStateManager() - .createAnimationToNewWorkspace(OVERVIEW, accuracy); + if (mWasLauncherAlreadyVisible) { + mLauncherTransitionController = mActivityControlHelper + .createControllerForVisibleActivity(activity); mLauncherTransitionController.dispatchOnStart(); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN); } else { TraceHelper.beginSection("WTS-init"); - mLauncher.getStateManager().goToState(OVERVIEW, false); - TraceHelper.partitionSection("WTS-init", "State changed"); - // TODO: Implement a better animation for fading in - View rootView = mLauncher.getRootView(); + View rootView = activity.getRootView(); rootView.setAlpha(0); rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { @@ -430,21 +411,18 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { TraceHelper.endSection("WTS-init", "Launcher frame is drawn"); rootView.post(() -> rootView.getViewTreeObserver().removeOnDrawListener(this)); - if (launcher != mLauncher) { + if (activity != mActivity) { return; } mStateCallback.setState(STATE_LAUNCHER_DRAWN); } }); - - // Optimization, hide the all apps view to prevent layout while initializing - mLauncher.getAppsView().setVisibility(View.GONE); } mRecentsView.showTask(mRunningTaskId); mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */); - mLauncherLayoutListener.open(); + mLayoutListener.open(); } public void setLauncherOnDrawCallback(Runnable callback) { @@ -452,7 +430,7 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } private void launcherFrameDrawn() { - View rootView = mLauncher.getRootView(); + View rootView = mActivity.getRootView(); if (rootView.getAlpha() < 1) { if (mGestureStarted) { final MultiStateCallback callback = mStateCallback; @@ -471,7 +449,7 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } private void initializeLauncherAnimationController() { - mLauncherLayoutListener.setHandler(this); + mLayoutListener.setHandler(this); onLauncherLayoutChanged(); final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs; @@ -508,7 +486,7 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } private void onQuickInteractionStart() { - mLauncher.getStateManager().goToState(FAST_OVERVIEW, + mActivityControlHelper.onQuickInteractionStart(mActivity, mWasLauncherAlreadyVisible || mGestureStarted); mQuickScrubController.onQuickScrubStart(false); } @@ -523,32 +501,14 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } /** - * Called by {@link #mLauncherLayoutListener} when launcher layout changes + * Called by {@link #mLayoutListener} when launcher layout changes */ public void onLauncherLayoutChanged() { - initTransitionEndpoints(mLauncher.getDeviceProfile()); + initTransitionEndpoints(mActivity.getDeviceProfile()); if (!mWasLauncherAlreadyVisible) { - float startProgress; - AllAppsTransitionController controller = mLauncher.getAllAppsController(); - - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { - startProgress = 1; - } else { - float scrollRange = Math.max(controller.getShiftRange(), 1); - startProgress = (mTransitionDragLength / scrollRange) + 1; - } - AnimatorSet anim = new AnimatorSet(); - ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS, - startProgress, OVERVIEW.getVerticalProgress(mLauncher)); - shiftAnim.setInterpolator(LINEAR); - anim.play(shiftAnim); - - // TODO: Link this animation to state animation, so that it is cancelled - // automatically on state change - anim.setDuration(mTransitionDragLength * 2); - mLauncherTransitionController = - AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2); + mLauncherTransitionController = mActivityControlHelper + .createControllerForHiddenActivity(mActivity, mTransitionDragLength); mLauncherTransitionController.setPlayFraction(mCurrentShift.value); } } @@ -661,14 +621,13 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } } } - mRecentsAnimationWrapper.setController(controller, apps); setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); } public void onRecentsAnimationCanceled() { mRecentsAnimationWrapper.setController(null, null); - clearReference(); + mActivityInitListener.unregister(); setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); } @@ -684,9 +643,10 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { * on both background and UI threads */ private void notifyGestureStarted() { - final Launcher curLauncher = mLauncher; - if (curLauncher != null) { - curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible); + final T curActivity = mActivity; + if (curActivity != null) { + mActivityControlHelper.onQuickstepGestureStarted( + curActivity, mWasLauncherAlreadyVisible); } } @@ -770,25 +730,20 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { mGestureEndCallback.run(); } - clearReference(); + mActivityInitListener.unregister(); mInputConsumer.unregisterInputConsumer(); } private void invalidateHandlerWithLauncher() { mLauncherTransitionController = null; - mLauncherLayoutListener.setHandler(null); - mLauncherLayoutListener.close(false); - - // Restore the requested orientation to the user preference after the gesture has ended - mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE); + mLayoutListener.finish(); mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */); } private void resetStateForAnimationCancel() { - LauncherState startState = mLauncher.getStateManager().getRestState(); - boolean animate = mWasLauncherAlreadyVisible || mGestureStarted; - mLauncher.getStateManager().goToState(startState, animate); + boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted; + mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible); } public void layoutListenerClosed() { @@ -817,17 +772,8 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { if (taskView != null) { // Defer finishing the animation until the next launcher frame with the // new thumbnail - ViewOnDrawExecutor executor = new ViewOnDrawExecutor() { - @Override - public void onViewDetachedFromWindow(View v) { - if (!isCompleted()) { - runAllTasks(); - } - } - }; - executor.attachTo(mLauncher, taskView, - false /* waitForLoadAnimation */); - executor.execute(finishTransitionRunnable); + mActivityControlHelper.executeOnNextDraw(mActivity, taskView, + finishTransitionRunnable); finishTransitionPosted = true; } } @@ -843,8 +789,7 @@ public class WindowTransformSwipeHandler extends InternalStateHandler { } private void setupLauncherUiAfterSwipeUpAnimation() { - // Re apply state in case we did something funky during the transition. - mLauncher.getStateManager().reapplyState(); + mActivityControlHelper.onSwipeUpComplete(mActivity); // Animate the first icon. mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */); diff --git a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java similarity index 83% rename from quickstep/src/com/android/quickstep/LauncherLayoutListener.java rename to quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java index fbdbe7a141..6b7143d048 100644 --- a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java +++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.views; + +import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import android.graphics.Rect; import android.view.MotionEvent; @@ -21,11 +23,14 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; +import com.android.quickstep.ActivityControlHelper.LayoutListener; +import com.android.quickstep.WindowTransformSwipeHandler; /** * Floating view which shows the task snapshot allowing it to be dragged and placed. */ -public class LauncherLayoutListener extends AbstractFloatingView implements Insettable { +public class LauncherLayoutListener extends AbstractFloatingView + implements Insettable, LayoutListener { private final Launcher mLauncher; private WindowTransformSwipeHandler mHandler; @@ -36,6 +41,7 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse setVisibility(INVISIBLE); } + @Override public void setHandler(WindowTransformSwipeHandler handler) { mHandler = handler; } @@ -65,6 +71,7 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse } } + @Override public void open() { if (!mIsOpen) { mLauncher.getDragLayer().addView(this); @@ -86,4 +93,11 @@ public class LauncherLayoutListener extends AbstractFloatingView implements Inse protected boolean isOfType(int type) { return (type & TYPE_QUICKSTEP_PREVIEW) != 0; } + + @Override + public void finish() { + setHandler(null); + close(false); + mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE); + } } diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 35edaf4668..458f7b280f 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -55,6 +55,8 @@ public abstract class BaseDraggingActivity extends BaseActivity { private ActionMode mCurrentActionMode; protected boolean mIsSafeModeEnabled; + private OnStartCallback mOnStartCallback; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -85,6 +87,8 @@ public abstract class BaseDraggingActivity extends BaseActivity { public abstract T getOverviewPanel(); + public abstract View getRootView(); + public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info); public abstract void invalidateParent(ItemInfo info); @@ -188,4 +192,26 @@ public abstract class BaseDraggingActivity extends BaseActivity { protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) { return false; } + + @Override + protected void onStart() { + super.onStart(); + + if (mOnStartCallback != null) { + mOnStartCallback.onActivityStart(this); + mOnStartCallback = null; + } + } + + public void setOnStartCallback(OnStartCallback callback) { + mOnStartCallback = callback; + } + + /** + * Callback for listening for onStart + */ + public interface OnStartCallback { + + void onActivityStart(T activity); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a38ce078ab..ee6dd591c1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -209,7 +209,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L @Thunk boolean mWorkspaceLoading = true; - private OnStartCallback mOnStartCallback; private OnResumeCallback mOnResumeCallback; private ViewOnDrawExecutor mPendingExecutor; @@ -767,10 +766,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L super.onStart(); FirstFrameAnimatorHelper.setIsVisible(true); - if (mOnStartCallback != null) { - mOnStartCallback.onLauncherStart(this); - mOnStartCallback = null; - } if (mLauncherCallbacks != null) { mLauncherCallbacks.onStart(); } @@ -1177,6 +1172,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L return mAllAppsController; } + @Override public LauncherRootView getRootView() { return (LauncherRootView) mLauncherView; } @@ -1775,10 +1771,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L mOnResumeCallback = callback; } - public void setOnStartCallback(OnStartCallback callback) { - mOnStartCallback = callback; - } - /** * Implementation of the method from LauncherModel.Callbacks. */ @@ -2450,12 +2442,4 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, L void onLauncherResume(); } - - /** - * Callback for listening for onStart - */ - public interface OnStartCallback { - - void onLauncherStart(Launcher launcher); - } }