summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java')
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java335
1 files changed, 335 insertions, 0 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
new file mode 100644
index 0000000000..445a23bf3f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -0,0 +1,335 @@
+/*
+ * 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.Utilities.squaredHypot;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.AnimatedFloat.VALUE;
+
+import android.graphics.Rect;
+import android.util.FloatProperty;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.folder.FolderIcon;
+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_KEYGUARD = 1;
+ public static final int ALPHA_INDEX_STASH = 2;
+ public static final int ALPHA_INDEX_RECENTS_DISABLED = 3;
+ public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
+ private static final int NUM_ALPHA_CHANNELS = 5;
+
+ 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 AnimatedFloat mTaskbarNavButtonTranslationY;
+
+ 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, NUM_ALPHA_CHANNELS);
+ 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);
+
+ mModelCallbacks.init(controllers);
+ if (mActivity.isUserSetupComplete()) {
+ // Only load the callbacks if user setup is completed
+ LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
+ }
+ mTaskbarNavButtonTranslationY =
+ controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
+ }
+
+ 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);
+ }
+
+ /**
+ * Should be called when the recents button is disabled, so we can hide taskbar icons as well.
+ */
+ public void setRecentsButtonDisabled(boolean isDisabled) {
+ // TODO: check TaskbarStashController#supportsStashing(), to stash instead of setting alpha.
+ mTaskbarIconAlpha.getProperty(ALPHA_INDEX_RECENTS_DISABLED).setValue(isDisabled ? 0 : 1);
+ }
+
+ /**
+ * Sets OnClickListener and OnLongClickListener for the given view.
+ */
+ public void setClickAndLongClickListenersForIcon(View icon) {
+ mTaskbarView.setClickAndLongClickListenersForIcon(icon);
+ }
+
+ /**
+ * Adds one time pre draw listener to the taskbar view, it is called before
+ * drawing a frame and invoked only once
+ * @param listener callback that will be invoked before drawing the next frame
+ */
+ public void addOneTimePreDrawListener(Runnable listener) {
+ mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver();
+ if (viewTreeObserver.isAlive()) {
+ listener.run();
+ viewTreeObserver.removeOnPreDrawListener(this);
+ }
+ return true;
+ }
+ });
+ }
+
+ public Rect getIconLayoutBounds() {
+ return mTaskbarView.getIconLayoutBounds();
+ }
+
+ public View[] getIconViews() {
+ return mTaskbarView.getIconViews();
+ }
+
+ 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);
+ setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
+
+ int collapsedHeight = mActivity.getDefaultTaskbarWindowHeight();
+ int expandedHeight = Math.max(collapsedHeight,
+ mActivity.getDeviceProfile().taskbarSize + 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, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+ }
+
+ AnimatorPlaybackController controller = setter.createPlaybackController();
+ mOnControllerPreCreateCallback = () -> controller.setPlayFraction(0);
+ return controller;
+ }
+
+ public void onRotationChanged(DeviceProfile deviceProfile) {
+ if (areIconsVisible()) {
+ // We only translate on rotation when on home
+ return;
+ }
+ mTaskbarNavButtonTranslationY.updateValue(-deviceProfile.getTaskbarOffsetY());
+ }
+
+ /**
+ * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
+ * touch bounds.
+ */
+ public boolean isEventOverAnyItem(MotionEvent ev) {
+ return mTaskbarView.isEventOverAnyItem(ev);
+ }
+
+ /**
+ * Callbacks for {@link TaskbarView} to interact with its controller.
+ */
+ public class TaskbarViewCallbacks {
+ private final float mSquaredTouchSlop = Utilities.squaredTouchSlop(mActivity);
+
+ private float mDownX, mDownY;
+ private boolean mCanceledStashHint;
+
+ public View.OnClickListener getIconOnClickListener() {
+ return mActivity.getItemOnClickListener();
+ }
+
+ public View.OnLongClickListener getIconOnLongClickListener() {
+ return mControllers.taskbarDragController::startDragOnLongClick;
+ }
+
+ public View.OnLongClickListener getBackgroundOnLongClickListener() {
+ return view -> mControllers.taskbarStashController
+ .updateAndAnimateIsManuallyStashedInApp(true);
+ }
+
+ /**
+ * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
+ * consume the touch so TaskbarView treats it as an ACTION_CANCEL.
+ */
+ public boolean onTouchEvent(MotionEvent motionEvent) {
+ final float x = motionEvent.getRawX();
+ final float y = motionEvent.getRawY();
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownX = x;
+ mDownY = y;
+ mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
+ mCanceledStashHint = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (!mCanceledStashHint
+ && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
+ mControllers.taskbarStashController.startStashHint(
+ /* animateForward= */ false);
+ mCanceledStashHint = true;
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (!mCanceledStashHint) {
+ mControllers.taskbarStashController.startStashHint(
+ /* animateForward= */ false);
+ }
+ break;
+ }
+ return false;
+ }
+ }
+
+ public static final FloatProperty<View> ICON_TRANSLATE_X =
+ new FloatProperty<View>("taskbarAligmentTranslateX") {
+
+ @Override
+ public void setValue(View view, float v) {
+ if (view instanceof BubbleTextView) {
+ ((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
+ } else if (view instanceof FolderIcon) {
+ ((FolderIcon) view).setTranslationForTaskbarAlignmentAnimation(v);
+ } else {
+ view.setTranslationX(v);
+ }
+ }
+
+ @Override
+ public Float get(View view) {
+ if (view instanceof BubbleTextView) {
+ return ((BubbleTextView) view)
+ .getTranslationXForTaskbarAlignmentAnimation();
+ } else if (view instanceof FolderIcon) {
+ return ((FolderIcon) view).getTranslationXForTaskbarAlignmentAnimation();
+ }
+ return view.getTranslationX();
+ }
+ };
+}