mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-11 06:44:00 +00:00
Log: - Taskbar app launch (also from foldeR) - Taskbar app drag (also from folder) - Taskbar folder open - Long press to hide taskbar - Long press to show taskbar - Overview Split screen action Also add support for ActivityContext to overwrite/add to LauncherAtom.ItemInfo, which TaskbarActivityContext does to change HotseatContainer and PredictedHotseatContainer to TaskBarContainer Test: enable logcat locally Bug: 193009817 Change-Id: Ia82c846a727fecb8cbfd0a069c8af1276083bf83
356 lines
14 KiB
Java
356 lines
14 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.LauncherState.HOTSEAT_ICONS;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
|
|
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
|
|
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.ObjectAnimator;
|
|
import android.graphics.Rect;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import com.android.launcher3.BaseQuickstepLauncher;
|
|
import com.android.launcher3.DeviceProfile;
|
|
import com.android.launcher3.LauncherState;
|
|
import com.android.launcher3.QuickstepTransitionManager;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.anim.AnimatorListeners;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.logging.InstanceId;
|
|
import com.android.launcher3.logging.InstanceIdSequence;
|
|
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
|
import com.android.launcher3.util.MultiValueAlpha;
|
|
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
|
import com.android.launcher3.util.OnboardingPrefs;
|
|
import com.android.quickstep.AnimatedFloat;
|
|
import com.android.quickstep.RecentsAnimationCallbacks;
|
|
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
|
|
import com.android.quickstep.RecentsAnimationController;
|
|
import com.android.quickstep.SystemUiProxy;
|
|
import com.android.quickstep.views.RecentsView;
|
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.stream.Stream;
|
|
|
|
/**
|
|
* A data source which integrates with a Launcher instance
|
|
*/
|
|
public class LauncherTaskbarUIController extends TaskbarUIController {
|
|
|
|
private final BaseQuickstepLauncher mLauncher;
|
|
private final TaskbarStateHandler mTaskbarStateHandler;
|
|
|
|
private final TaskbarActivityContext mContext;
|
|
private final TaskbarDragLayer mTaskbarDragLayer;
|
|
private final TaskbarView mTaskbarView;
|
|
|
|
private final AnimatedFloat mIconAlignmentForResumedState =
|
|
new AnimatedFloat(this::onIconAlignmentRatioChanged);
|
|
private final AnimatedFloat mIconAlignmentForGestureState =
|
|
new AnimatedFloat(this::onIconAlignmentRatioChanged);
|
|
|
|
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
|
|
this::onStashedInAppChanged;
|
|
|
|
// Initialized in init.
|
|
private TaskbarControllers mControllers;
|
|
private AnimatedFloat mTaskbarBackgroundAlpha;
|
|
private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
|
|
private AlphaProperty mIconAlphaForHome;
|
|
private boolean mIsAnimatingToLauncherViaResume;
|
|
private boolean mIsAnimatingToLauncherViaGesture;
|
|
private TaskbarKeyguardController mKeyguardController;
|
|
|
|
private LauncherState mTargetStateOverride = null;
|
|
|
|
public LauncherTaskbarUIController(
|
|
BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
|
|
mContext = context;
|
|
mTaskbarDragLayer = context.getDragLayer();
|
|
mTaskbarView = mTaskbarDragLayer.findViewById(R.id.taskbar_view);
|
|
|
|
mLauncher = launcher;
|
|
mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
|
|
}
|
|
|
|
@Override
|
|
protected void init(TaskbarControllers taskbarControllers) {
|
|
mControllers = taskbarControllers;
|
|
|
|
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
|
|
.getTaskbarBackgroundAlpha();
|
|
mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
|
|
.getOverrideBackgroundAlpha();
|
|
|
|
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
|
|
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
|
|
|
|
mLauncher.setTaskbarUIController(this);
|
|
mKeyguardController = taskbarControllers.taskbarKeyguardController;
|
|
|
|
onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
|
|
mIconAlignmentForResumedState.finishAnimation();
|
|
onIconAlignmentRatioChanged();
|
|
|
|
onStashedInAppChanged(mLauncher.getDeviceProfile());
|
|
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
onLauncherResumedOrPaused(false);
|
|
mIconAlignmentForResumedState.finishAnimation();
|
|
mIconAlignmentForGestureState.finishAnimation();
|
|
|
|
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
|
|
mLauncher.getHotseat().setIconsAlpha(1f);
|
|
mLauncher.setTaskbarUIController(null);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isTaskbarTouchable() {
|
|
return !isAnimatingToLauncher() && !mControllers.taskbarStashController.isStashed();
|
|
}
|
|
|
|
private boolean isAnimatingToLauncher() {
|
|
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
|
|
}
|
|
|
|
@Override
|
|
protected void updateContentInsets(Rect outContentInsets) {
|
|
int contentHeight = mControllers.taskbarStashController.getContentHeight();
|
|
outContentInsets.top = mTaskbarDragLayer.getHeight() - contentHeight;
|
|
}
|
|
|
|
/**
|
|
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
|
|
*/
|
|
public void onLauncherResumedOrPaused(boolean isResumed) {
|
|
if (mKeyguardController.isScreenOff()) {
|
|
if (!isResumed) {
|
|
return;
|
|
} else {
|
|
// Resuming implicitly means device unlocked
|
|
mKeyguardController.setScreenOn();
|
|
}
|
|
}
|
|
|
|
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
|
|
ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
|
|
getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
|
|
.setDuration(duration);
|
|
|
|
anim.addListener(AnimatorListeners.forEndCallback(
|
|
() -> mIsAnimatingToLauncherViaResume = false));
|
|
anim.start();
|
|
mIsAnimatingToLauncherViaResume = isResumed;
|
|
|
|
TaskbarStashController stashController = mControllers.taskbarStashController;
|
|
stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
|
|
stashController.applyState(duration);
|
|
SystemUiProxy.INSTANCE.get(mContext).notifyTaskbarStatus(!isResumed,
|
|
mControllers.taskbarStashController.isStashedInApp());
|
|
}
|
|
|
|
/**
|
|
* Create Taskbar animation when going from an app to Launcher as part of recents transition.
|
|
* @param toState If known, the state we will end up in when reaching Launcher.
|
|
* @param callbacks callbacks to track the recents animation lifecycle. The state change is
|
|
* automatically reset once the recents animation finishes
|
|
*/
|
|
public Animator createAnimToLauncher(@NonNull LauncherState toState,
|
|
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
|
|
TaskbarStashController stashController = mControllers.taskbarStashController;
|
|
ObjectAnimator animator = mIconAlignmentForGestureState
|
|
.animateToValue(1)
|
|
.setDuration(duration);
|
|
animator.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
mTargetStateOverride = null;
|
|
animator.removeListener(this);
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationStart(Animator animation) {
|
|
mTargetStateOverride = toState;
|
|
mIsAnimatingToLauncherViaGesture = true;
|
|
// TODO: FLAG_IN_APP might be sufficient for now, but in the future we do want to
|
|
// add another flag for LauncherState as well. We will need to decide whether to
|
|
// show hotseat or the task bar.
|
|
stashController.updateStateForFlag(FLAG_IN_APP, false);
|
|
stashController.applyState(duration);
|
|
}
|
|
});
|
|
|
|
TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
|
|
callbacks.addListener(listener);
|
|
RecentsView recentsView = mLauncher.getOverviewPanel();
|
|
recentsView.setTaskLaunchListener(() -> {
|
|
listener.endGestureStateOverride(true);
|
|
callbacks.removeListener(listener);
|
|
});
|
|
|
|
return animator;
|
|
}
|
|
|
|
private float getCurrentIconAlignmentRatio() {
|
|
return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
|
|
}
|
|
|
|
private void onIconAlignmentRatioChanged() {
|
|
if (mControllers == null) {
|
|
return;
|
|
}
|
|
float alignment = getCurrentIconAlignmentRatio();
|
|
mControllers.taskbarViewController.setLauncherIconAlignment(
|
|
alignment, mLauncher.getDeviceProfile());
|
|
|
|
mTaskbarBackgroundAlpha.updateValue(1 - alignment);
|
|
|
|
LauncherState state = mTargetStateOverride != null ? mTargetStateOverride
|
|
: mLauncher.getStateManager().getState();
|
|
if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
|
// If the hotseat icons are visible, then switch taskbar in last frame
|
|
setTaskbarViewVisible(alignment < 1);
|
|
} else {
|
|
mLauncher.getHotseat().setIconsAlpha(1);
|
|
mIconAlphaForHome.setValue(1 - alignment);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param ev MotionEvent in screen coordinates.
|
|
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
|
|
*/
|
|
public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
|
|
return mTaskbarView.isEventOverAnyItem(ev);
|
|
}
|
|
|
|
public boolean isDraggingItem() {
|
|
return mContext.getDragController().isDragging();
|
|
}
|
|
|
|
public View getRootView() {
|
|
return mTaskbarDragLayer;
|
|
}
|
|
|
|
private void setTaskbarViewVisible(boolean isVisible) {
|
|
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
|
|
mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
|
|
}
|
|
|
|
@Override
|
|
protected void onStashedInAppChanged() {
|
|
onStashedInAppChanged(mLauncher.getDeviceProfile());
|
|
if (mControllers.taskbarStashController.isStashedInApp()) {
|
|
mContext.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE);
|
|
} else {
|
|
mContext.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
|
|
}
|
|
}
|
|
|
|
private void onStashedInAppChanged(DeviceProfile deviceProfile) {
|
|
boolean taskbarStashedInApps = mControllers.taskbarStashController.isStashedInApp();
|
|
deviceProfile.isTaskbarPresentInApps = !taskbarStashedInApps;
|
|
}
|
|
|
|
/**
|
|
* Sets whether the background behind the taskbar/nav bar should be hidden.
|
|
*/
|
|
public void forceHideBackground(boolean forceHide) {
|
|
mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
|
|
}
|
|
|
|
@Override
|
|
public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
|
|
return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
|
|
}
|
|
|
|
/**
|
|
* Starts the taskbar education flow, if the user hasn't seen it yet.
|
|
*/
|
|
public void showEdu() {
|
|
if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()
|
|
|| Utilities.IS_RUNNING_IN_TEST_HARNESS
|
|
|| mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN)) {
|
|
return;
|
|
}
|
|
mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN);
|
|
|
|
mControllers.taskbarEduController.showEdu();
|
|
}
|
|
|
|
/**
|
|
* Manually ends the taskbar education flow.
|
|
*/
|
|
public void hideEdu() {
|
|
if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()) {
|
|
return;
|
|
}
|
|
|
|
mControllers.taskbarEduController.hideEdu();
|
|
}
|
|
|
|
@Override
|
|
public void onTaskbarIconLaunched(WorkspaceItemInfo item) {
|
|
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
|
|
mLauncher.logAppLaunch(mContext.getStatsLogManager(), item, instanceId);
|
|
}
|
|
|
|
private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener {
|
|
private final RecentsAnimationCallbacks mCallbacks;
|
|
|
|
TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
|
|
mCallbacks = callbacks;
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
|
endGestureStateOverride(true);
|
|
}
|
|
|
|
@Override
|
|
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
|
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
|
|
}
|
|
|
|
private void endGestureStateOverride(boolean finishedToApp) {
|
|
mCallbacks.removeListener(this);
|
|
mIsAnimatingToLauncherViaGesture = false;
|
|
|
|
mIconAlignmentForGestureState
|
|
.animateToValue(0)
|
|
.start();
|
|
|
|
TaskbarStashController controller = mControllers.taskbarStashController;
|
|
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
|
|
controller.applyState();
|
|
}
|
|
}
|
|
}
|