blob: 719c2ae78caee7e73f3c7576f723ea3fae443504 [file] [log] [blame]
/*
* 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 com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* {@link BaseActivityInterface} for the in-launcher recents.
*/
public final class LauncherActivityInterface extends
BaseActivityInterface<LauncherState, BaseQuickstepLauncher> {
public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
private LauncherActivityInterface() {
super(true, OVERVIEW, BACKGROUND_APP);
}
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect,
PagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {
return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
}
}
@Override
public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
// When going to home, the state animator we use has SKIP_OVERVIEW because we assume that
// setRecentsAttachedToAppWindow() will handle animating Overview instead. Thus, at the end
// of the animation, we should ensure recents is at the correct position for NORMAL state.
// For example, when doing a long swipe to home, RecentsView may be scaled down. This is
// relatively expensive, so do it on the next frame instead of critical path.
MAIN_EXECUTOR.getHandler().post(launcher.getStateManager()::reapplyState);
launcher.getRootView().setForceHideBackArrow(false);
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
}
@Override
public void onAssistantVisibilityChanged(float visibility) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
launcher.onAssistantVisibilityChanged(visibility);
}
@Override
public void onOneHandedModeStateChanged(boolean activated) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
launcher.onOneHandedStateChanged(activated);
}
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
protected void createBackgroundToOverviewAnim(BaseQuickstepLauncher activity,
PendingAnimation pa) {
super.createBackgroundToOverviewAnim(activity, pa);
// Animate the blur and wallpaper zoom
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
pa.addFloat(getDepthController(),
new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
}
};
BaseQuickstepLauncher launcher = factory.initUI();
// Since all apps is not visible, we can safely reset the scroll position.
// This ensures then the next swipe up to all-apps starts from scroll 0.
launcher.getAppsView().reset(false /* animate */);
return factory;
}
@Override
public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
return new LauncherInitListener((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome));
}
@Override
public void setOnDeferredActivityLaunchCallback(Runnable r) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
launcher.setOnDeferredActivityLaunchCallback(r);
}
@Nullable
@Override
public BaseQuickstepLauncher getCreatedActivity() {
return BaseQuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
}
@Nullable
@Override
public DepthController getDepthController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
return launcher.getDepthController();
}
@Nullable
@Override
public LauncherTaskbarUIController getTaskbarController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
return launcher.getTaskbarUIController();
}
@Nullable
@Override
public RecentsView getVisibleRecentsView() {
Launcher launcher = getVisibleLauncher();
RecentsView recentsView =
launcher != null && launcher.getStateManager().getState().overviewUi
? launcher.getOverviewPanel() : null;
if (recentsView == null || (!launcher.hasBeenResumed()
&& recentsView.getRunningTaskViewId() == -1)) {
// If live tile has ended, return null.
return null;
}
return recentsView;
}
@Nullable
@UiThread
private Launcher getVisibleLauncher() {
Launcher launcher = getCreatedActivity();
return (launcher != null) && launcher.isStarted()
&& ((ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())
|| launcher.hasBeenResumed()) ? launcher : null;
}
@Override
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode()) {
RecentsView recentsView = getVisibleRecentsView();
if (recentsView == null) {
return false;
}
}
closeOverlay();
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(),
onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
return true;
}
@Override
public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
final StateManager<LauncherState> stateManager = getCreatedActivity().getStateManager();
stateManager.addStateListener(
new StateManager.StateListener<LauncherState>() {
@Override
public void onStateTransitionComplete(LauncherState toState) {
// Are we going from Recents to Workspace?
if (toState == LauncherState.NORMAL) {
exitRunnable.run();
notifyRecentsOfOrientation(deviceState);
stateManager.removeStateListener(this);
}
}
});
}
private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
// reset layout on swipe to home
RecentsView recentsView = getCreatedActivity().getOverviewPanel();
recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
rotationTouchHelper.getDisplayRotation());
}
@Override
public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
return homeBounds;
}
@Override
public boolean allowMinimizeSplitScreen() {
return true;
}
@Override
public boolean isInLiveTileMode() {
Launcher launcher = getCreatedActivity();
return launcher != null && launcher.getStateManager().getState() == OVERVIEW &&
launcher.isStarted();
}
@Override
public void onLaunchTaskFailed() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
launcher.getStateManager().goToState(OVERVIEW);
}
@Override
public void closeOverlay() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
return;
}
LauncherOverlayManager om = launcher.getOverlayManager();
if (!launcher.isStarted() || launcher.isForceInvisible()) {
om.hideOverlay(false /* animate */);
} else {
om.hideOverlay(150);
}
LauncherTaskbarUIController taskbarController = getTaskbarController();
if (taskbarController != null) {
taskbarController.hideEdu();
}
}
@Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
LauncherTaskbarUIController uiController = getTaskbarController();
Animator superAnimator = super.getParallelAnimationToLauncher(
endTarget, duration, callbacks);
if (uiController == null || callbacks == null) {
return superAnimator;
}
LauncherState toState = stateFromGestureEndTarget(endTarget);
Animator taskbarAnimator = uiController.createAnimToLauncher(toState, callbacks, duration);
if (superAnimator == null) {
return taskbarAnimator;
} else {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(superAnimator, taskbarAnimator);
return animatorSet;
}
}
@Override
protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
LauncherState state) {
return state.getWorkspaceScrimColor(launcher);
}
@Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
return super.deferStartingActivity(deviceState, ev);
}
return uiController.isEventOverAnyTaskbarItem(ev);
}
@Override
public boolean shouldCancelCurrentGesture() {
LauncherTaskbarUIController uiController = getTaskbarController();
if (uiController == null) {
return super.shouldCancelCurrentGesture();
}
return uiController.isDraggingItem();
}
@Override
public LauncherState stateFromGestureEndTarget(GestureEndTarget endTarget) {
switch (endTarget) {
case RECENTS:
return OVERVIEW;
case NEW_TASK:
case LAST_TASK:
return QUICK_SWITCH;
case HOME:
default:
return NORMAL;
}
}
}