/* * 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.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.AnimatedFloat.VALUE; import android.graphics.Rect; import android.view.View; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.AnimatedFloat; /** * Handles properties/data collection, then passes the results to TaskbarView to render. */ public class TaskbarViewController { private static final Runnable NO_OP = () -> { }; public static final int ALPHA_INDEX_HOME = 0; public static final int ALPHA_INDEX_IME = 1; public static final int ALPHA_INDEX_KEYGUARD = 2; public static final int ALPHA_INDEX_STASH = 3; private final TaskbarActivityContext mActivity; private final TaskbarView mTaskbarView; private final MultiValueAlpha mTaskbarIconAlpha; private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale); private final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat( this::updateTranslationY); private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat( this::updateTranslationY); private final TaskbarModelCallbacks mModelCallbacks; // Initialized in init. private TaskbarControllers mControllers; // Animation to align icons with Launcher, created lazily. This allows the controller to be // active only during the animation and does not need to worry about layout changes. private AnimatorPlaybackController mIconAlignControllerLazy = null; private Runnable mOnControllerPreCreateCallback = NO_OP; public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) { mActivity = activity; mTaskbarView = taskbarView; mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4); mTaskbarIconAlpha.setUpdateVisibility(true); mModelCallbacks = new TaskbarModelCallbacks(activity, mTaskbarView); } public void init(TaskbarControllers controllers) { mControllers = controllers; mTaskbarView.init(new TaskbarViewCallbacks()); mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize; mTaskbarIconScaleForStash.updateValue(1f); LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks); } public void onDestroy() { LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks); } public boolean areIconsVisible() { return mTaskbarView.areIconsVisible(); } public MultiValueAlpha getTaskbarIconAlpha() { return mTaskbarIconAlpha; } /** * Should be called when the IME visibility changes, so we can make Taskbar not steal touches. */ public void setImeIsVisible(boolean isImeVisible) { mTaskbarView.setTouchesEnabled(!isImeVisible); } /** * Sets OnClickListener and OnLongClickListener for the given view. */ public void setClickAndLongClickListenersForIcon(View icon) { mTaskbarView.setClickAndLongClickListenersForIcon(icon); } public Rect getIconLayoutBounds() { return mTaskbarView.getIconLayoutBounds(); } public AnimatedFloat getTaskbarIconScaleForStash() { return mTaskbarIconScaleForStash; } public AnimatedFloat getTaskbarIconTranslationYForStash() { return mTaskbarIconTranslationYForStash; } /** * Applies scale properties for the entire TaskbarView (rather than individual icons). */ private void updateScale() { float scale = mTaskbarIconScaleForStash.value; mTaskbarView.setScaleX(scale); mTaskbarView.setScaleY(scale); } private void updateTranslationY() { mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value + mTaskbarIconTranslationYForStash.value); } /** * Sets the taskbar icon alignment relative to Launcher hotseat icons * @param alignmentRatio [0, 1] * 0 => not aligned * 1 => fully aligned */ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { if (mIconAlignControllerLazy == null) { mIconAlignControllerLazy = createIconAlignmentController(launcherDp); } mIconAlignControllerLazy.setPlayFraction(alignmentRatio); if (alignmentRatio <= 0 || alignmentRatio >= 1) { // Cleanup lazy controller so that it is created again in next animation mIconAlignControllerLazy = null; } } /** * Creates an animation for aligning the taskbar icons with the provided Launcher device profile */ private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) { mOnControllerPreCreateCallback.run(); PendingAnimation setter = new PendingAnimation(100); Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity); float scaleUp = ((float) launcherDp.iconSizePx) / mActivity.getDeviceProfile().iconSizePx; int hotseatCellSize = (launcherDp.availableWidthPx - hotseatPadding.left - hotseatPadding.right) / launcherDp.numShownHotseatIcons; int offsetY = launcherDp.getTaskbarOffsetY(); setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR); int collapsedHeight = mActivity.getDeviceProfile().taskbarSize; int expandedHeight = collapsedHeight + offsetY; setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight( anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight)); int count = mTaskbarView.getChildCount(); for (int i = 0; i < count; i++) { View child = mTaskbarView.getChildAt(i); ItemInfo info = (ItemInfo) child.getTag(); setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR); float childCenter = (child.getLeft() + child.getRight()) / 2; float hotseatIconCenter = hotseatPadding.left + hotseatCellSize * info.screenId + hotseatCellSize / 2; setter.setFloat(child, VIEW_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR); } AnimatorPlaybackController controller = setter.createPlaybackController(); mOnControllerPreCreateCallback = () -> controller.setPlayFraction(0); return controller; } /** * Callbacks for {@link TaskbarView} to interact with its controller. */ public class TaskbarViewCallbacks { public View.OnClickListener getIconOnClickListener() { return mActivity::onTaskbarIconClicked; } public View.OnLongClickListener getIconOnLongClickListener() { return mControllers.taskbarDragController::startDragOnLongClick; } public View.OnLongClickListener getBackgroundOnLongClickListener() { return view -> mControllers.taskbarStashController.updateAndAnimateIsStashedInApp(true); } } }