blob: e32aaee5324588af9fe91d17e15fdb2fdc2965ea [file] [log] [blame]
/*
* 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.fallback;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
@TargetApi(Build.VERSION_CODES.R)
public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
implements StateListener<RecentsState> {
private static final int TASK_DISMISS_DURATION = 150;
@Nullable
private Task mHomeTask;
public FallbackRecentsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
mActivity.getStateManager().addStateListener(this);
}
@Override
public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
super.init(actionsView, splitController);
setOverviewStateEnabled(true);
setOverlayEnabled(true);
}
@Override
public void startHome() {
mActivity.startHome();
AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
/**
* When starting gesture interaction from home, we add a temporary invisible tile corresponding
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
* from a foreground task.
*/
public void onGestureAnimationStartOnHome(Task[] homeTask,
RotationTouchHelper rotationTouchHelper) {
// TODO(b/195607777) General fallback love, but this might be correct
// Home task should be defined as the front-most task info I think?
mHomeTask = homeTask.length > 0 ? homeTask[0] : null;
onGestureAnimationStart(homeTask, rotationTouchHelper);
}
/**
* When the gesture ends and we're going to recents view, we also remove the temporary
* invisible tile added for the home task. This also pushes the remaining tiles back
* to the center.
*/
@Override
public void onPrepareGestureEndAnimation(
@Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
TaskViewSimulator[] taskViewSimulators) {
super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
if (mHomeTask != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTask.key.id);
if (tv != null) {
PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
createTaskDismissAnimation(pa, tv, true, false,
TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
pa.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pa.createPlaybackController();
controller.dispatchOnStart();
animatorSet.play(controller.getAnimationPlayer());
}
}
}
@Override
public void onGestureAnimationEnd() {
if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.HOME) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
}
super.onGestureAnimationEnd();
}
@Override
public void setCurrentTask(int runningTaskViewId) {
super.setCurrentTask(runningTaskViewId);
int runningTaskId = getTaskIdsForRunningTaskView()[0];
if (mHomeTask != null && mHomeTask.key.id != runningTaskId) {
mHomeTask = null;
setRunningTaskHidden(false);
}
}
@Nullable
@Override
protected TaskView getHomeTaskView() {
return mHomeTask != null ? getTaskViewByTaskId(mHomeTask.key.id) : null;
}
@Override
protected boolean shouldAddStubTaskView(Task[] runningTasks) {
if (runningTasks.length > 1) {
// can't be in split screen w/ home task
return super.shouldAddStubTaskView(runningTasks);
}
Task runningTask = runningTasks[0];
if (mHomeTask != null && runningTask != null
&& mHomeTask.key.id == runningTask.key.id
&& getTaskViewCount() == 0 && mLoadPlanEverApplied) {
// Do not add a stub task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a stub task and later removing it.
// Ignore empty task signal if applyLoadPlan has never run.
return false;
}
return super.shouldAddStubTaskView(runningTasks);
}
@Override
protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
// TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
int runningTaskId = getTaskIdsForRunningTaskView()[0];
if (mHomeTask != null && mHomeTask.key.id == runningTaskId
&& !taskGroups.isEmpty()) {
// Check if the task list has running task
boolean found = false;
for (GroupTask group : taskGroups) {
if (group.containsTask(runningTaskId)) {
found = true;
break;
}
}
if (!found) {
ArrayList<GroupTask> newList = new ArrayList<>(taskGroups.size() + 1);
newList.addAll(taskGroups);
newList.add(new GroupTask(mHomeTask, null, null));
taskGroups = newList;
}
}
super.applyLoadPlan(taskGroups);
}
@Override
public void setRunningTaskHidden(boolean isHidden) {
if (mHomeTask != null) {
// Always keep the home task hidden
isHidden = true;
}
super.setRunningTaskHidden(isHidden);
}
@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);
resetModalVisuals();
}
}
}
@Override
public void initiateSplitSelect(TaskView taskView,
@SplitConfigurationOptions.StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}
@Override
public void onStateTransitionStart(RecentsState toState) {
setOverviewStateEnabled(true);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.isFullScreen());
if (toState == MODAL_TASK) {
setOverviewSelectEnabled(true);
}
setFreezeViewVisibility(true);
}
@Override
public void onStateTransitionComplete(RecentsState finalState) {
if (finalState == HOME) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
}
boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
setFreezeViewVisibility(false);
if (finalState != MODAL_TASK) {
setOverviewSelectEnabled(false);
}
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
}
@Override
public void setOverviewStateEnabled(boolean enabled) {
super.setOverviewStateEnabled(enabled);
if (enabled) {
RecentsState state = mActivity.getStateManager().getState();
setDisallowScrollToClearAll(!state.hasClearAllButton());
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// Do not let touch escape to siblings below this view.
return result || mActivity.getStateManager().getState().overviewUi();
}
@Override
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}
@Override
protected boolean canLaunchFullscreenTask() {
return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
}
}