diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 357e9ecd6f..fa0d3f347b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides.states; import android.content.Context; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Launcher; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -54,11 +55,7 @@ public class BackgroundAppState extends OverviewState { @Override public float[] getOverviewScaleAndOffset(Launcher launcher) { - return new float[] {getOverviewScale(launcher), NO_OFFSET}; - } - - private float getOverviewScale(Launcher launcher) { - return ((RecentsView) launcher.getOverviewPanel()).getMaxScaleForFullScreen(); + return getOverviewScaleAndOffsetForBackgroundState(launcher); } @Override @@ -88,4 +85,11 @@ public class BackgroundAppState extends OverviewState { protected float getDepthUnchecked(Context context) { return 1f; } + + public static float[] getOverviewScaleAndOffsetForBackgroundState( + BaseDraggingActivity activity) { + return new float[] { + ((RecentsView) activity.getOverviewPanel()).getMaxScaleForFullScreen(), + NO_OFFSET}; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java index a7e7d3a436..414d38954e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; +import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -49,22 +50,25 @@ public class OverviewModalTaskState extends OverviewState { @Override public float[] getOverviewScaleAndOffset(Launcher launcher) { - Resources res = launcher.getBaseContext().getResources(); - - Rect out = new Rect(); - launcher.getOverviewPanel().getTaskSize(out); - int taskHeight = out.height(); - - float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin); - float bottomMargin = res.getDimension(R.dimen.overview_actions_top_margin); - float newHeight = taskHeight + topMargin + bottomMargin; - float scale = newHeight / taskHeight; - - return new float[] {scale, 0}; + return getOverviewScaleAndOffsetForModalState(launcher); } @Override public float getOverviewModalness() { return 1.0f; } + + public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) { + Resources res = activity.getResources(); + + Rect out = new Rect(); + activity.getOverviewPanel().getTaskSize(out); + int taskHeight = out.height(); + float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin); + float bottomMargin = res.getDimension(R.dimen.overview_actions_top_margin); + float newHeight = taskHeight + topMargin + bottomMargin; + float scale = newHeight / taskHeight; + + return new float[] {scale, NO_OFFSET}; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java index 324c390edf..cd546e2b31 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java @@ -15,14 +15,15 @@ */ package com.android.quickstep; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; -import static com.android.quickstep.fallback.FallbackRecentsView.ZOOM_PROGRESS; +import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP; +import static com.android.quickstep.fallback.RecentsState.DEFAULT; import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; +import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Rect; @@ -30,6 +31,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.util.ActivityInitListener; @@ -89,15 +91,13 @@ public final class FallbackActivityInterface implements public AnimationFactory prepareRecentsUI( boolean activityVisible, Consumer callback) { RecentsActivity activity = getCreatedActivity(); - if (activityVisible) { + if (activity == null) { return (transitionLength) -> { }; } + activity.getStateManager().goToState(BACKGROUND_APP); FallbackRecentsView rv = activity.getOverviewPanel(); rv.setContentAlpha(0); - rv.getClearAllButton().setVisibilityAlpha(0); - rv.setDisallowScrollToClearAll(true); - rv.setInOverviewState(false); return new AnimationFactory() { @@ -115,23 +115,19 @@ public final class FallbackActivityInterface implements @Override public void createActivityInterface(long transitionLength) { - AnimatorSet animatorSet = new AnimatorSet(); + PendingAnimation pa = new PendingAnimation(transitionLength * 2); + if (isAnimatingToRecents) { - ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1); - anim.setDuration(transitionLength).setInterpolator(LINEAR); - animatorSet.play(anim); + pa.addFloat(rv, CONTENT_ALPHA, 0, 1, LINEAR); } - ObjectAnimator anim = ObjectAnimator.ofFloat(rv, ZOOM_PROGRESS, 1, 0); - anim.setDuration(transitionLength).setInterpolator(LINEAR); - animatorSet.play(anim); - - AnimatorPlaybackController controller = - AnimatorPlaybackController.wrap(animatorSet, transitionLength); + pa.addFloat(rv, SCALE_PROPERTY, rv.getMaxScaleForFullScreen(), 1, LINEAR); + pa.addFloat(rv, FULLSCREEN_PROGRESS, 1, 0, LINEAR); + AnimatorPlaybackController controller = pa.createPlaybackController(); // Since we are changing the start position of the UI, reapply the state, at the end - controller.setEndAction(() -> - rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5)); + controller.setEndAction(() -> activity.getStateManager().goToState( + controller.getInterpolatedProgress() > 0.5 ? DEFAULT : BACKGROUND_APP)); callback.accept(controller); } }; @@ -147,7 +143,7 @@ public final class FallbackActivityInterface implements @Nullable @Override public RecentsActivity getCreatedActivity() { - return BaseRecentsActivity.ACTIVITY_TRACKER.getCreatedActivity(); + return RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity(); } @Nullable diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index 8ce6bbccfa..77e50caae7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -37,10 +37,12 @@ import android.os.Bundle; import android.util.ArrayMap; import android.view.MotionEvent; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.util.ObjectWrapper; +import com.android.quickstep.BaseActivityInterface.AnimationFactory; import com.android.quickstep.GestureState.GestureEndTarget; import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.util.RectFSpringAnim; @@ -103,6 +105,12 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler { }; + public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState, InputConsumerController inputConsumer, boolean isLikelyToStartNewTask, boolean continuingLastGesture) { @@ -165,10 +173,6 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler t * mDragLengthFactor); + mLauncherTransitionController.dispatchOnStart(); + updateLauncherTransitionProgress(); + } + + private void updateLauncherTransitionProgress() { + if (mLauncherTransitionController == null + || !canCreateNewOrUpdateExistingLauncherTransitionController()) { + return; + } + // Normalize the progress to 0 to 1, as the animation controller will clamp it to that + // anyway. The controller mimics the drag length factor by applying it to its interpolators. + float progress = mCurrentShift.value / mDragLengthFactor; + mLauncherTransitionController.setPlayFraction(progress); + } + + /** + * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME + * (it has its own animation) or if we're already animating the current controller. + * @return Whether we can create the launcher controller or update its progress. + */ + private boolean canCreateNewOrUpdateExistingLauncherTransitionController() { + return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted; + } + @Override protected boolean moveWindowWithRecentsScroll() { return mInQuickSwitchMode; @@ -260,6 +303,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler { public static final String EXTRA_THUMBNAIL = "thumbnailData"; public static final String EXTRA_TASK_ID = "taskID"; + public static final ActivityTracker ACTIVITY_TRACKER = + new ActivityTracker<>(); private Handler mUiHandler = new Handler(Looper.getMainLooper()); private RecentsRootView mRecentsRootView; private FallbackRecentsView mFallbackRecentsView; + private OverviewActionsView mActionsView; - @Override + private Configuration mOldConfig; + + private StateManager mStateManager; + + /** + * Init drag layer and overview panel views. + */ protected void initViews() { setContentView(R.layout.fallback_recents_activity); mRecentsRootView = findViewById(R.id.drag_layer); mFallbackRecentsView = findViewById(R.id.overview_panel); + mActionsView = findViewById(R.id.overview_actions_view); + mRecentsRootView.recreateControllers(); - mFallbackRecentsView.init(findViewById(R.id.overview_actions_view)); + mFallbackRecentsView.init(mActionsView); } @Override @@ -103,25 +131,38 @@ public final class RecentsActivity extends BaseRecentsActivity { intent.removeExtra(EXTRA_TASK_ID); intent.removeExtra(EXTRA_THUMBNAIL); super.onNewIntent(intent); + ACTIVITY_TRACKER.handleNewIntent(this, intent); } - @Override + /** + * Logic for when device configuration changes (rotation, screen size change, multi-window, + * etc.) + */ protected void onHandleConfigChanged() { - super.onHandleConfigChanged(); + mUserEventDispatcher = null; + initDeviceProfile(); + + AbstractFloatingView.closeOpenViews(this, true, + AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); + dispatchDeviceProfileChanged(); + + reapplyUi(); mRecentsRootView.recreateControllers(); } - @Override - protected void reapplyUi() { - mRecentsRootView.dispatchInsets(); - } - - @Override + /** + * Generate the device profile to use in this activity. + * @return device profile + */ protected DeviceProfile createDeviceProfile() { DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this); + DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this); + + // In case we are reusing IDP, create a copy so that we don't conflict with Launcher + // activity. return (mRecentsRootView != null) && isInMultiWindowMode() ? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize()) - : super.createDeviceProfile(); + : dp1.copy(this); } @Override @@ -139,6 +180,10 @@ public final class RecentsActivity extends BaseRecentsActivity { return (T) mFallbackRecentsView; } + public OverviewActionsView getActionsView() { + return mActionsView; + } + @Override public void returnToHomescreen() { super.returnToHomescreen(); @@ -160,12 +205,7 @@ public final class RecentsActivity extends BaseRecentsActivity { RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) { AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets, wallpaperTargets); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mFallbackRecentsView.resetViewUI(); - } - }); + anim.addListener(resetStateListener()); result.setAnimation(anim, RecentsActivity.this); } }; @@ -193,12 +233,7 @@ public final class RecentsActivity extends BaseRecentsActivity { .createAdjacentPageAnimForTaskLaunch(taskView); adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION); - adjacentAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mFallbackRecentsView.resetTaskVisuals(); - } - }); + adjacentAnimation.addListener(resetStateListener()); target.play(adjacentAnimation); } return target; @@ -210,13 +245,14 @@ public final class RecentsActivity extends BaseRecentsActivity { // onActivityStart callback. mFallbackRecentsView.setContentAlpha(1); super.onStart(); - mFallbackRecentsView.resetTaskVisuals(); } @Override protected void onStop() { super.onStop(); - mFallbackRecentsView.reset(); + + // Workaround for b/78520668, explicitly trim memory once UI is hidden + onTrimMemory(TRIM_MEMORY_UI_HIDDEN); } @Override @@ -228,4 +264,98 @@ public final class RecentsActivity extends BaseRecentsActivity { public void onTaskLaunched() { mFallbackRecentsView.resetTaskVisuals(); } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mStateManager = new StateManager<>(this, RecentsState.DEFAULT); + + mOldConfig = new Configuration(getResources().getConfiguration()); + initDeviceProfile(); + initViews(); + + getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, + Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); + ACTIVITY_TRACKER.handleCreate(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + int diff = newConfig.diff(mOldConfig); + if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { + onHandleConfigChanged(); + } + mOldConfig.setTo(newConfig); + super.onConfigurationChanged(newConfig); + } + + /** + * Initialize/update the device profile. + */ + private void initDeviceProfile() { + mDeviceProfile = createDeviceProfile(); + onDeviceProfileInitiated(); + } + + @Override + public void onEnterAnimationComplete() { + super.onEnterAnimationComplete(); + // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled + // as a part of quickstep, so that high-res thumbnails can load the next time we enter + // overview + RecentsModel.INSTANCE.get(this).getThumbnailCache() + .getHighResLoadingState().setVisible(true); + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + RecentsModel.INSTANCE.get(this).onTrimMemory(level); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + ACTIVITY_TRACKER.onActivityDestroyed(this); + } + + @Override + public void onBackPressed() { + // TODO: Launch the task we came from + startHome(); + } + + public void startHome() { + startActivity(new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + @Override + protected StateHandler[] createStateHandlers() { + return new StateHandler[] { new FallbackRecentsStateController(this) }; + } + + @Override + public StateManager getStateManager() { + return mStateManager; + } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + super.dump(prefix, fd, writer, args); + writer.println(prefix + "Misc:"); + dumpMisc(prefix + "\t", writer); + } + + private AnimatorListenerAdapter resetStateListener() { + return new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mFallbackRecentsView.resetTaskVisuals(); + mStateManager.reapplyState(); + } + }; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java new file mode 100644 index 0000000000..3f1e7bad9b --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.fallback; + +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK; +import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE; +import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; +import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; +import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; + +import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.statemanager.StateManager.StateHandler; +import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.util.MultiValueAlpha; +import com.android.quickstep.RecentsActivity; +import com.android.quickstep.views.ClearAllButton; + +/** + * State controller for fallback recents activity + */ +public class FallbackRecentsStateController implements StateHandler { + + private final StateAnimationConfig mNoConfig = new StateAnimationConfig(); + private final RecentsActivity mActivity; + private final FallbackRecentsView mRecentsView; + + public FallbackRecentsStateController(RecentsActivity activity) { + mActivity = activity; + mRecentsView = activity.getOverviewPanel(); + } + + @Override + public void setState(RecentsState state) { + mRecentsView.updateEmptyMessage(); + mRecentsView.resetTaskVisuals(); + setProperties(state, mNoConfig, PropertySetter.NO_ANIM_PROPERTY_SETTER); + } + + @Override + public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config, + PendingAnimation setter) { + if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) { + // The entire recents animation is played atomically. + return; + } + if (config.hasAnimationFlag(SKIP_OVERVIEW)) { + return; + } + // While animating into recents, update the visible task data as needed + setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData); + mRecentsView.updateEmptyMessage(); + + setProperties(toState, config, setter); + } + + private void setProperties(RecentsState state, StateAnimationConfig config, + PropertySetter setter) { + float buttonAlpha = state.hasButtons() ? 1 : 0; + setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA, + buttonAlpha, LINEAR); + setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(), + MultiValueAlpha.VALUE, buttonAlpha, LINEAR); + + float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity); + setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], + config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR)); + setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], + config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR)); + + setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(), + config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR)); + setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java index 559004c3e4..1ab317b9eb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -15,17 +15,17 @@ */ package com.android.quickstep.fallback; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.quickstep.fallback.RecentsState.DEFAULT; +import static com.android.quickstep.fallback.RecentsState.MODAL_TASK; import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY; +import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; -import android.graphics.Canvas; +import android.os.Build; import android.util.AttributeSet; -import android.util.FloatProperty; -import android.view.View; -import com.android.launcher3.Utilities; +import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.quickstep.RecentsActivity; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; @@ -34,26 +34,9 @@ import com.android.systemui.shared.recents.model.Task.TaskKey; import java.util.ArrayList; -public class FallbackRecentsView extends RecentsView { - - public static final FloatProperty ZOOM_PROGRESS = - new FloatProperty ("zoomInProgress") { - - @Override - public void setValue(FallbackRecentsView view, float value) { - view.setZoomProgress(value); - } - - @Override - public Float get(FallbackRecentsView view) { - return view.mZoomInProgress; - } - }; - - private float mZoomInProgress = 0; - private boolean mInOverviewState = true; - - private float mZoomScale = 1f; +@TargetApi(Build.VERSION_CODES.R) +public class FallbackRecentsView extends RecentsView + implements StateListener { private RunningTaskInfo mRunningTaskInfo; @@ -63,6 +46,7 @@ public class FallbackRecentsView extends RecentsView { public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY); + mActivity.getStateManager().addStateListener(this); } @Override @@ -77,71 +61,12 @@ public class FallbackRecentsView extends RecentsView { mActivity.startHome(); } - @Override - public void onViewAdded(View child) { - super.onViewAdded(child); - updateEmptyMessage(); - } - - @Override - public void onViewRemoved(View child) { - super.onViewRemoved(child); - updateEmptyMessage(); - } - - @Override - public void draw(Canvas canvas) { - maybeDrawEmptyMessage(canvas); - super.draw(canvas); - } - - @Override - public void reset() { - super.reset(); - resetViewUI(); - } - @Override public boolean shouldUseMultiWindowTaskSizeStrategy() { // Just use the activity task size for multi-window as well. return false; } - public void resetViewUI() { - setZoomProgress(0); - resetTaskVisuals(); - } - - public void setInOverviewState(boolean inOverviewState) { - if (mInOverviewState != inOverviewState) { - mInOverviewState = inOverviewState; - if (mInOverviewState) { - resetTaskVisuals(); - } else { - setZoomProgress(1); - } - } - } - - @Override - public void resetTaskVisuals() { - super.resetTaskVisuals(); - setFullscreenProgress(mFullscreenProgress); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mZoomScale = getMaxScaleForFullScreen(); - setZoomProgress(mZoomInProgress); - } - - public void setZoomProgress(float progress) { - mZoomInProgress = progress; - SCALE_PROPERTY.set(this, Utilities.mapRange(mZoomInProgress, 1, mZoomScale)); - FULLSCREEN_PROGRESS.set(this, mZoomInProgress); - } - public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) { mRunningTaskInfo = runningTaskInfo; onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId); @@ -178,4 +103,37 @@ public class FallbackRecentsView extends RecentsView { } super.applyLoadPlan(tasks); } + + @Override + public void setModalStateEnabled(boolean isModalState) { + super.setModalStateEnabled(isModalState); + if (isModalState) { + mActivity.getStateManager().goToState(RecentsState.MODAL_TASK); + } else { + if (mActivity.isInState(RecentsState.MODAL_TASK)) { + mActivity.getStateManager().goToState(DEFAULT); + } + } + } + + @Override + public void onStateTransitionStart(RecentsState toState) { + setOverviewStateEnabled(true); + setFreezeViewVisibility(true); + } + + @Override + public void onStateTransitionComplete(RecentsState finalState) { + setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK); + setFreezeViewVisibility(false); + } + + @Override + public void setOverviewStateEnabled(boolean enabled) { + super.setOverviewStateEnabled(enabled); + if (enabled) { + RecentsState state = mActivity.getStateManager().getState(); + setDisallowScrollToClearAll(!state.hasButtons()); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java new file mode 100644 index 0000000000..211a30c28d --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.fallback; + +import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState; +import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState; + +import android.content.Context; + +import com.android.launcher3.statemanager.BaseState; +import com.android.quickstep.RecentsActivity; + +/** + * State definition for Fallback recents + */ +public class RecentsState implements BaseState { + + private static final int FLAG_MODAL = BaseState.getFlag(0); + private static final int FLAG_HAS_BUTTONS = BaseState.getFlag(1); + private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2); + + public static final RecentsState DEFAULT = new RecentsState(0, FLAG_HAS_BUTTONS); + public static final RecentsState MODAL_TASK = new ModalState(1, + FLAG_DISABLE_RESTORE | FLAG_HAS_BUTTONS | FLAG_MODAL); + public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2, + FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN); + + public final int ordinal; + private final int mFlags; + + private static final float NO_OFFSET = 0; + private static final float NO_SCALE = 1; + + public RecentsState(int id, int flags) { + this.ordinal = id; + this.mFlags = flags; + } + + + @Override + public String toString() { + return "Ordinal-" + ordinal; + } + + @Override + public final boolean hasFlag(int mask) { + return (mFlags & mask) != 0; + } + + @Override + public int getTransitionDuration(Context context) { + return 250; + } + + @Override + public RecentsState getHistoryForState(RecentsState previousState) { + return DEFAULT; + } + + /** + * For this state, how modal should over view been shown. 0 modalness means all tasks drawn, + * 1 modalness means the current task is show on its own. + */ + public float getOverviewModalness() { + return hasFlag(FLAG_MODAL) ? 1 : 0; + } + + public boolean isFullScreen() { + return hasFlag(FLAG_FULL_SCREEN); + } + + public boolean hasButtons() { + return hasFlag(FLAG_HAS_BUTTONS); + } + + public float[] getOverviewScaleAndOffset(RecentsActivity activity) { + return new float[] { NO_SCALE, NO_OFFSET }; + } + + + private static class ModalState extends RecentsState { + + public ModalState(int id, int flags) { + super(id, flags); + } + + @Override + public float[] getOverviewScaleAndOffset(RecentsActivity activity) { + return getOverviewScaleAndOffsetForModalState(activity); + } + } + + private static class BackgroundAppState extends RecentsState { + public BackgroundAppState(int id, int flags) { + super(id, flags); + } + + @Override + public float[] getOverviewScaleAndOffset(RecentsActivity activity) { + return getOverviewScaleAndOffsetForBackgroundState(activity); + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 9d7efc4ddb..250c78bdac 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -31,11 +31,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Canvas; import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.View; import android.widget.FrameLayout; import com.android.launcher3.BaseQuickstepLauncher; @@ -123,24 +121,6 @@ public class LauncherRecentsView extends RecentsView } } - @Override - public void draw(Canvas canvas) { - maybeDrawEmptyMessage(canvas); - super.draw(canvas); - } - - @Override - public void onViewAdded(View child) { - super.onViewAdded(child); - updateEmptyMessage(); - } - - @Override - protected void onTaskStackUpdated() { - // Lazily update the empty message only when the task stack is reapplied - updateEmptyMessage(); - } - /** * Animates adjacent tasks and translate hotseat off screen as well. */ diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 700af97997..979e3ef15f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -550,6 +550,13 @@ public abstract class RecentsView extends PagedView impl child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL); updateTaskStartIndex(child); mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false); + updateEmptyMessage(); + } + + @Override + public void draw(Canvas canvas) { + maybeDrawEmptyMessage(canvas); + super.draw(canvas); } private void updateTaskStartIndex(View affectingView) { @@ -762,7 +769,10 @@ public abstract class RecentsView extends PagedView impl return taskViewCount; } - protected void onTaskStackUpdated() { } + protected void onTaskStackUpdated() { + // Lazily update the empty message only when the task stack is reapplied + updateEmptyMessage(); + } public void resetTaskVisuals() { for (int i = getTaskViewCount() - 1; i >= 0; i--) { diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java deleted file mode 100644 index 1b9158bc4f..0000000000 --- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2019 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 android.content.pm.ActivityInfo.CONFIG_ORIENTATION; -import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; - -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.R; -import com.android.launcher3.util.ActivityTracker; -import com.android.launcher3.util.SystemUiController; -import com.android.launcher3.util.Themes; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * A base fallback recents activity that provides support for device profile changes, activity - * lifecycle tracking, and basic input handling from recents. - * - * This class is only used as a fallback in case the default launcher does not have a recents - * implementation. - */ -public abstract class BaseRecentsActivity extends BaseDraggingActivity { - - public static final ActivityTracker ACTIVITY_TRACKER = - new ActivityTracker<>(); - private Configuration mOldConfig; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mOldConfig = new Configuration(getResources().getConfiguration()); - initDeviceProfile(); - initViews(); - - getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, - Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); - ACTIVITY_TRACKER.handleCreate(this); - } - - /** - * Init drag layer and overview panel views. - */ - abstract protected void initViews(); - - @Override - public void onConfigurationChanged(Configuration newConfig) { - int diff = newConfig.diff(mOldConfig); - if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { - onHandleConfigChanged(); - } - mOldConfig.setTo(newConfig); - super.onConfigurationChanged(newConfig); - } - - /** - * Logic for when device configuration changes (rotation, screen size change, multi-window, - * etc.) - */ - protected void onHandleConfigChanged() { - mUserEventDispatcher = null; - initDeviceProfile(); - - AbstractFloatingView.closeOpenViews(this, true, - AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); - dispatchDeviceProfileChanged(); - - reapplyUi(); - } - - /** - * Initialize/update the device profile. - */ - private void initDeviceProfile() { - mDeviceProfile = createDeviceProfile(); - onDeviceProfileInitiated(); - } - - /** - * Generate the device profile to use in this activity. - * @return device profile - */ - protected DeviceProfile createDeviceProfile() { - DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this); - - // In case we are reusing IDP, create a copy so that we don't conflict with Launcher - // activity. - return dp.copy(this); - } - - - @Override - protected void onStop() { - super.onStop(); - - // Workaround for b/78520668, explicitly trim memory once UI is hidden - onTrimMemory(TRIM_MEMORY_UI_HIDDEN); - } - - @Override - public void onEnterAnimationComplete() { - super.onEnterAnimationComplete(); - // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled - // as a part of quickstep, so that high-res thumbnails can load the next time we enter - // overview - RecentsModel.INSTANCE.get(this).getThumbnailCache() - .getHighResLoadingState().setVisible(true); - } - - @Override - public void onTrimMemory(int level) { - super.onTrimMemory(level); - RecentsModel.INSTANCE.get(this).onTrimMemory(level); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - ACTIVITY_TRACKER.handleNewIntent(this, intent); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - ACTIVITY_TRACKER.onActivityDestroyed(this); - } - - @Override - public void onBackPressed() { - // TODO: Launch the task we came from - startHome(); - } - - public void startHome() { - startActivity(new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_HOME) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } - - @Override - public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { - super.dump(prefix, fd, writer, args); - writer.println(prefix + "Misc:"); - dumpMisc(prefix + "\t", writer); - } -}