mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-19 10:48:19 +00:00
- Add LauncherState#isTaskbarAlignedWithHotseat() which defaults to !isTaskbarStashed(), but is always false for quick switch from home - Replaced FLAG_TRANSITION_STATE_START_STASHED and FLAG_TRANSITION_STATE_COMMITTED_STASHED with FLAG_STATE_TRANSITION_RUNNING and a reference to mLauncherState. STATE_START is equivalent to TRANSITION_RUNNING changing to true, and STATE_COMMITTED is equivalent to TRANSITION_RUNNING changing to false. Then can get details from the state such as whether taskbar is stashed and icon alignment from mLauncherState Test: quick switch from home, both with taskbar stashed in apps and not Fixes: 194728611 Bug: 204657916 Change-Id: I6cf84ec73a4036e14cc7268667c6f62100884c27
391 lines
16 KiB
Java
391 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2021 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.launcher3.taskbar;
|
|
|
|
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
|
|
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
|
|
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
|
|
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.AnimatorSet;
|
|
import android.animation.ObjectAnimator;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import com.android.launcher3.BaseQuickstepLauncher;
|
|
import com.android.launcher3.LauncherState;
|
|
import com.android.launcher3.statemanager.StateManager;
|
|
import com.android.launcher3.util.MultiValueAlpha;
|
|
import com.android.quickstep.AnimatedFloat;
|
|
import com.android.quickstep.RecentsAnimationCallbacks;
|
|
import com.android.quickstep.RecentsAnimationController;
|
|
import com.android.quickstep.views.RecentsView;
|
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Supplier;
|
|
|
|
/**
|
|
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
|
|
* the task bar accordingly.
|
|
*/
|
|
public class TaskbarLauncherStateController {
|
|
|
|
public static final int FLAG_RESUMED = 1 << 0;
|
|
public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
|
|
public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
|
|
|
|
/** Equivalent to an int with all 1s for binary operation purposes */
|
|
private static final int FLAGS_ALL = ~0;
|
|
|
|
private final AnimatedFloat mIconAlignmentForResumedState =
|
|
new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
|
|
private final AnimatedFloat mIconAlignmentForGestureState =
|
|
new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
|
|
private final AnimatedFloat mIconAlignmentForLauncherState =
|
|
new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
|
|
|
|
private TaskbarControllers mControllers;
|
|
private AnimatedFloat mTaskbarBackgroundAlpha;
|
|
private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
|
|
private BaseQuickstepLauncher mLauncher;
|
|
|
|
private Integer mPrevState;
|
|
private int mState;
|
|
private LauncherState mLauncherState = LauncherState.NORMAL;
|
|
|
|
private boolean mIsAnimatingToLauncherViaGesture;
|
|
private boolean mIsAnimatingToLauncherViaResume;
|
|
|
|
private final StateManager.StateListener<LauncherState> mStateListener =
|
|
new StateManager.StateListener<LauncherState>() {
|
|
|
|
@Override
|
|
public void onStateTransitionStart(LauncherState toState) {
|
|
if (toState != mLauncherState) {
|
|
// Treat FLAG_TRANSITION_STATE_RUNNING as a changed flag even if a previous
|
|
// state transition was already running, so we update the new target.
|
|
mPrevState &= ~FLAG_TRANSITION_STATE_RUNNING;
|
|
mLauncherState = toState;
|
|
}
|
|
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
|
|
applyState();
|
|
}
|
|
|
|
@Override
|
|
public void onStateTransitionComplete(LauncherState finalState) {
|
|
mLauncherState = finalState;
|
|
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
|
|
applyState();
|
|
}
|
|
};
|
|
|
|
public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
|
|
mControllers = controllers;
|
|
mLauncher = launcher;
|
|
|
|
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
|
|
.getTaskbarBackgroundAlpha();
|
|
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
|
|
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
|
|
mIconAlphaForHome.setConsumer(
|
|
(Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
|
|
|
|
mIconAlignmentForResumedState.finishAnimation();
|
|
onIconAlignmentRatioChangedForAppAndHomeTransition();
|
|
|
|
mLauncher.getStateManager().addStateListener(mStateListener);
|
|
}
|
|
|
|
public void onDestroy() {
|
|
mIconAlignmentForResumedState.finishAnimation();
|
|
mIconAlignmentForGestureState.finishAnimation();
|
|
mIconAlignmentForLauncherState.finishAnimation();
|
|
|
|
mIconAlphaForHome.setConsumer(null);
|
|
mLauncher.getHotseat().setIconsAlpha(1f);
|
|
mLauncher.getStateManager().removeStateListener(mStateListener);
|
|
}
|
|
|
|
public Animator createAnimToLauncher(@NonNull LauncherState toState,
|
|
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
|
|
// If going to overview, stash the task bar
|
|
// If going home, align the icons to hotseat
|
|
AnimatorSet animatorSet = new AnimatorSet();
|
|
|
|
// Update stashed flags first to ensure goingToUnstashedLauncherState() returns correctly.
|
|
TaskbarStashController stashController = mControllers.taskbarStashController;
|
|
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
|
|
toState.isTaskbarStashed(mLauncher));
|
|
stashController.updateStateForFlag(FLAG_IN_APP, false);
|
|
|
|
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
|
|
animatorSet.play(stashController.applyStateWithoutStart(duration));
|
|
animatorSet.play(applyState(duration, false));
|
|
|
|
TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
|
|
callbacks.addListener(listener);
|
|
RecentsView recentsView = mLauncher.getOverviewPanel();
|
|
recentsView.setTaskLaunchListener(() -> {
|
|
listener.endGestureStateOverride(true);
|
|
callbacks.removeListener(listener);
|
|
});
|
|
return animatorSet;
|
|
}
|
|
|
|
public boolean isAnimatingToLauncher() {
|
|
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
|
|
}
|
|
|
|
/**
|
|
* Updates the proper flag to change the state of the task bar.
|
|
*
|
|
* Note that this only updates the flag. {@link #applyState()} needs to be called separately.
|
|
*
|
|
* @param flag The flag to update.
|
|
* @param enabled Whether to enable the flag
|
|
*/
|
|
public void updateStateForFlag(int flag, boolean enabled) {
|
|
if (enabled) {
|
|
mState |= flag;
|
|
} else {
|
|
mState &= ~flag;
|
|
}
|
|
}
|
|
|
|
private boolean hasAnyFlag(int flagMask) {
|
|
return hasAnyFlag(mState, flagMask);
|
|
}
|
|
|
|
private boolean hasAnyFlag(int flags, int flagMask) {
|
|
return (flags & flagMask) != 0;
|
|
}
|
|
|
|
public void applyState() {
|
|
applyState(TASKBAR_STASH_DURATION);
|
|
}
|
|
|
|
public void applyState(long duration) {
|
|
applyState(duration, true);
|
|
}
|
|
|
|
public Animator applyState(boolean start) {
|
|
return applyState(TASKBAR_STASH_DURATION, start);
|
|
}
|
|
|
|
public Animator applyState(long duration, boolean start) {
|
|
Animator animator = null;
|
|
if (mPrevState == null || mPrevState != mState) {
|
|
// If this is our initial state, treat all flags as changed.
|
|
int changedFlags = mPrevState == null ? FLAGS_ALL : mPrevState ^ mState;
|
|
mPrevState = mState;
|
|
animator = onStateChangeApplied(changedFlags, duration, start);
|
|
}
|
|
return animator;
|
|
}
|
|
|
|
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
|
|
AnimatorSet animatorSet = new AnimatorSet();
|
|
if (hasAnyFlag(changedFlags, FLAG_RESUMED)) {
|
|
boolean isResumed = isResumed();
|
|
ObjectAnimator anim = mIconAlignmentForResumedState
|
|
.animateToValue(isResumed && goingToUnstashedLauncherState()
|
|
? 1 : 0)
|
|
.setDuration(duration);
|
|
|
|
anim.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
mIsAnimatingToLauncherViaResume = false;
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationStart(Animator animation) {
|
|
mIsAnimatingToLauncherViaResume = isResumed;
|
|
|
|
TaskbarStashController stashController = mControllers.taskbarStashController;
|
|
stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
|
|
stashController.applyState(duration);
|
|
}
|
|
});
|
|
animatorSet.play(anim);
|
|
}
|
|
|
|
if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) {
|
|
boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
|
|
Animator animator = mIconAlignmentForGestureState
|
|
.animateToValue(isRecentsAnimationRunning && goingToUnstashedLauncherState()
|
|
? 1 : 0);
|
|
if (isRecentsAnimationRunning) {
|
|
animator.setDuration(duration);
|
|
}
|
|
animator.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
mIsAnimatingToLauncherViaGesture = false;
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationStart(Animator animation) {
|
|
mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
|
|
}
|
|
});
|
|
animatorSet.play(animator);
|
|
}
|
|
|
|
if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
|
|
boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
|
|
animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
|
|
.setDuration(duration));
|
|
}
|
|
|
|
if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
|
|
boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
|
|
playStateTransitionAnim(animatorSet, duration, committed);
|
|
|
|
if (committed && mLauncherState == LauncherState.QUICK_SWITCH) {
|
|
// We're about to be paused, set immediately to ensure seamless handoff.
|
|
updateStateForFlag(FLAG_RESUMED, false);
|
|
applyState(0 /* duration */);
|
|
}
|
|
}
|
|
|
|
if (start) {
|
|
animatorSet.start();
|
|
}
|
|
return animatorSet;
|
|
}
|
|
|
|
/** Returns whether we're going to a state where taskbar icons should align with launcher. */
|
|
private boolean goingToUnstashedLauncherState() {
|
|
return !mControllers.taskbarStashController.isInStashedLauncherState();
|
|
}
|
|
|
|
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
|
|
boolean committed) {
|
|
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
|
|
float toAlignment = mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) ? 1 : 0;
|
|
|
|
TaskbarStashController controller = mControllers.taskbarStashController;
|
|
controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
|
|
Animator stashAnimator = controller.applyStateWithoutStart(duration);
|
|
if (stashAnimator != null) {
|
|
stashAnimator.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
if (isInStashedState && committed) {
|
|
// Reset hotseat alpha to default
|
|
mLauncher.getHotseat().setIconsAlpha(1);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationStart(Animator animation) {
|
|
if (mLauncher.getHotseat().getIconsAlpha() > 0) {
|
|
mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
|
|
}
|
|
}
|
|
});
|
|
animatorSet.play(stashAnimator);
|
|
}
|
|
|
|
animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
|
|
.setDuration(duration));
|
|
}
|
|
|
|
private boolean isResumed() {
|
|
return (mState & FLAG_RESUMED) != 0;
|
|
}
|
|
|
|
private boolean isRecentsAnimationRunning() {
|
|
return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
|
|
}
|
|
|
|
private void onIconAlignmentRatioChangedForStateTransition() {
|
|
if (!isResumed()) {
|
|
return;
|
|
}
|
|
onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState);
|
|
}
|
|
|
|
private void onIconAlignmentRatioChangedForAppAndHomeTransition() {
|
|
onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
|
|
}
|
|
|
|
private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) {
|
|
if (mControllers == null) {
|
|
return;
|
|
}
|
|
float alignment = alignmentSupplier.get();
|
|
mControllers.taskbarViewController.setLauncherIconAlignment(
|
|
alignment, mLauncher.getDeviceProfile());
|
|
|
|
// Switch taskbar and hotseat in last frame
|
|
setTaskbarViewVisible(alignment < 1);
|
|
}
|
|
|
|
private float getCurrentIconAlignmentRatioBetweenAppAndHome() {
|
|
return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
|
|
}
|
|
|
|
private float getCurrentIconAlignmentRatioForLauncherState() {
|
|
return mIconAlignmentForLauncherState.value;
|
|
}
|
|
|
|
private void setTaskbarViewVisible(boolean isVisible) {
|
|
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
|
|
}
|
|
|
|
private final class TaskBarRecentsAnimationListener implements
|
|
RecentsAnimationCallbacks.RecentsAnimationListener {
|
|
private final RecentsAnimationCallbacks mCallbacks;
|
|
|
|
TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
|
|
mCallbacks = callbacks;
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
|
|
endGestureStateOverride(true);
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
|
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
|
|
}
|
|
|
|
private void endGestureStateOverride(boolean finishedToApp) {
|
|
mCallbacks.removeListener(this);
|
|
|
|
// Update the resumed state immediately to ensure a seamless handoff
|
|
boolean launcherResumed = !finishedToApp;
|
|
mIconAlignmentForResumedState.updateValue(launcherResumed ? 1 : 0);
|
|
|
|
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
|
|
updateStateForFlag(FLAG_RESUMED, launcherResumed);
|
|
applyState();
|
|
|
|
|
|
TaskbarStashController controller = mControllers.taskbarStashController;
|
|
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
|
|
controller.applyState();
|
|
}
|
|
}
|
|
}
|