diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index c0765ee111..32250787b3 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -292,6 +292,7 @@ 150dp 225dp 300dp + 40dp 24dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 593605f9fb..bf5740dcac 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -24,6 +24,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN; +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING; +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN; import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW; import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; @@ -75,6 +77,7 @@ import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.PopupDataProvider; +import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; import com.android.launcher3.testing.TestLogging; @@ -303,7 +306,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { type, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SLIPPERY - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); windowLayoutParams.setTitle(WINDOW_TITLE); windowLayoutParams.packageName = getPackageName(); @@ -467,7 +472,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { @Override public void onDragEnd() { - maybeSetTaskbarWindowNotFullscreen(); + onDragEndOrViewRemoved(); } @Override @@ -571,25 +576,34 @@ public class TaskbarActivityContext extends BaseTaskbarContext { .updateValue(darkIntensity); } + /** + * Called to update a {@link AutohideSuspendFlag} with a new value. + */ + public void setAutohideSuspendFlag(@AutohideSuspendFlag int flag, boolean newValue) { + mControllers.taskbarAutohideSuspendController.updateFlag(flag, newValue); + } + /** * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. */ public void setTaskbarWindowFullscreen(boolean fullscreen) { - mControllers.taskbarAutohideSuspendController.updateFlag( - TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen); + setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, fullscreen); mIsFullscreen = fullscreen; setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : mLastRequestedNonFullscreenHeight); } /** - * Reverts Taskbar window to its original size, if all floating views are closed and there is - * no system drag operation in progress. + * Called when drag ends or when a view is removed from the DragLayer. */ - void maybeSetTaskbarWindowNotFullscreen() { - if (AbstractFloatingView.getAnyView(this, TYPE_ALL) == null - && !mControllers.taskbarDragController.isSystemDragInProgress()) { + void onDragEndOrViewRemoved() { + boolean isDragInProgress = mControllers.taskbarDragController.isSystemDragInProgress(); + + if (!isDragInProgress && !AbstractFloatingView.hasOpenView(this, TYPE_ALL)) { + // Reverts Taskbar window to its original size setTaskbarWindowFullscreen(false); } + + setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_DRAGGING, isDragInProgress); } public boolean isTaskbarWindowFullscreen() { @@ -703,6 +717,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { Task task = (Task) tag; ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, ActivityOptions.makeBasic()); + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } else if (tag instanceof FolderInfo) { FolderIcon folderIcon = (FolderIcon) view; Folder folder = folderIcon.getFolder(); @@ -762,6 +777,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } mControllers.uiController.onTaskbarIconLaunched(info); + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) .show(); @@ -771,6 +787,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } else if (tag instanceof AppInfo) { startItemInfoActivity((AppInfo) tag); mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag); + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } else { Log.e(TAG, "Unknown type clicked: " + tag); } @@ -805,6 +822,20 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); } + /** + * Called when we want to unstash taskbar when user performs swipes up gesture. + */ + public void onSwipeToUnstashTaskbar() { + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false); + } + + /** + * Called when a transient Autohide flag suspend status changes. + */ + public void onTransientAutohideSuspendFlagChanged(boolean isSuspended) { + mControllers.taskbarStashController.updateTaskbarTimeout(isSuspended); + } + /** * Called when we detect a motion down or up/cancel in the nav region while stashed. * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java index 3cf9c997c9..4350e9c280 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java @@ -33,21 +33,28 @@ import java.util.StringJoiner; public class TaskbarAutohideSuspendController implements TaskbarControllers.LoggableTaskbarController { + // Taskbar window is fullscreen. public static final int FLAG_AUTOHIDE_SUSPEND_FULLSCREEN = 1 << 0; + // User is dragging item. public static final int FLAG_AUTOHIDE_SUSPEND_DRAGGING = 1 << 1; + // User has touched down but has not lifted finger. + public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2; @IntDef(flag = true, value = { FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, FLAG_AUTOHIDE_SUSPEND_DRAGGING, + FLAG_AUTOHIDE_SUSPEND_TOUCHING, }) @Retention(RetentionPolicy.SOURCE) public @interface AutohideSuspendFlag {} + private final TaskbarActivityContext mActivity; private final SystemUiProxy mSystemUiProxy; private @AutohideSuspendFlag int mAutohideSuspendFlags = 0; public TaskbarAutohideSuspendController(TaskbarActivityContext activity) { + mActivity = activity; mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity); } @@ -59,12 +66,27 @@ public class TaskbarAutohideSuspendController implements * Adds or removes the given flag, then notifies system UI proxy whether to suspend auto-hide. */ public void updateFlag(@AutohideSuspendFlag int flag, boolean enabled) { + int flagsBefore = mAutohideSuspendFlags; if (enabled) { mAutohideSuspendFlags |= flag; } else { mAutohideSuspendFlags &= ~flag; } - mSystemUiProxy.notifyTaskbarAutohideSuspend(mAutohideSuspendFlags != 0); + if (flagsBefore == mAutohideSuspendFlags) { + // Nothing has changed, no need to notify. + return; + } + + boolean isSuspended = isSuspended(); + mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended); + mActivity.onTransientAutohideSuspendFlagChanged(isSuspended); + } + + /** + * Returns true iff taskbar autohide is currently suspended. + */ + public boolean isSuspended() { + return mAutohideSuspendFlags != 0; } @Override @@ -79,6 +101,7 @@ public class TaskbarAutohideSuspendController implements appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, "FLAG_AUTOHIDE_SUSPEND_FULLSCREEN"); appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_DRAGGING, "FLAG_AUTOHIDE_SUSPEND_DRAGGING"); + appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TOUCHING, "FLAG_AUTOHIDE_SUSPEND_TOUCHING"); return str.toString(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index 7e75779c62..7c9a13c5dc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -115,6 +115,14 @@ public class TaskbarDragLayer extends BaseDragLayer { return true; } + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mControllerCallbacks != null && ev.getAction() == MotionEvent.ACTION_OUTSIDE) { + mControllerCallbacks.onActionOutsideEvent(); + } + return super.onTouchEvent(ev); + } + @Override public void onViewRemoved(View child) { super.onViewRemoved(child); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index 353f1e03cb..13ecf81a7d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -23,6 +23,7 @@ import android.view.ViewTreeObserver; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.util.DimensionUtils; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; import com.android.quickstep.AnimatedFloat; @@ -165,11 +166,25 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo); } + /** + * Called whenever TaskbarDragLayer receives an ACTION_OUTSIDE event. + */ + public void onActionOutsideEvent() { + if (!DisplayController.isTransientTaskbar(mActivity)) { + return; + } + if (mControllers.taskbarStashController.isStashed()) { + return; + } + + mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); + } + /** * Called when a child is removed from TaskbarDragLayer. */ public void onDragLayerViewRemoved() { - mActivity.maybeSetTaskbarWindowNotFullscreen(); + mActivity.onDragEndOrViewRemoved(); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 64eb99e7d1..afd659fc6e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -39,11 +39,13 @@ import android.view.ViewConfiguration; import androidx.annotation.NonNull; import com.android.internal.jank.InteractionJankMonitor; +import com.android.launcher3.Alarm; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.SystemUiProxy; @@ -70,6 +72,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 7; // All apps is visible. public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed + public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar). // If any of these flags are enabled, isInApp should return true. private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; @@ -78,7 +81,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS - | FLAG_STASHED_SMALL_SCREEN; + | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO; private static final int FLAGS_STASHED_IN_APP_IGNORING_IME = FLAGS_STASHED_IN_APP & ~FLAG_STASHED_IN_APP_IME; @@ -132,6 +135,9 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba */ private static final boolean DEFAULT_STASHED_PREF = false; + // Auto stashes when user has not interacted with the Taskbar after X ms. + private static final long NO_TOUCH_TIMEOUT_TO_STASH_MS = 5000; + private final TaskbarActivityContext mActivity; private final SharedPreferences mPrefs; private final int mStashedHeight; @@ -162,6 +168,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba private boolean mEnableManualStashingDuringTests = false; + private final Alarm mTimeoutAlarm = new Alarm(); + // Evaluate whether the handle should be stashed private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder( flags -> { @@ -210,13 +218,16 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba StashedHandleViewController.ALPHA_INDEX_STASHED); mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale(); + boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity); // We use supportsVisualStashing() here instead of supportsManualStashing() because we want // it to work properly for tests that recreate taskbar. This check is here just to ensure // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false). boolean isManuallyStashedInApp = supportsVisualStashing() - && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF); + && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF) + && !isTransientTaskbar; boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible; updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp); + updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar); updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); updateStateForFlag(FLAG_IN_SETUP, isInSetup); updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode() @@ -243,7 +254,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba protected boolean supportsManualStashing() { return supportsVisualStashing() && isInApp() - && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests); + && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests) + && !DisplayController.isTransientTaskbar(mActivity); } /** @@ -376,6 +388,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba return mStashedHeight; } + /** + * Stash or unstashes the transient taskbar. + */ + public void updateAndAnimateTransientTaskbar(boolean stash) { + if (!DisplayController.isTransientTaskbar(mActivity)) { + return; + } + + if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) { + updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash); + applyState(); + } + } + /** * Should be called when long pressing the nav region when taskbar is present. * @return Whether taskbar was stashed and now is unstashed. @@ -549,11 +575,17 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public void onAnimationStart(Animator animation) { mIsStashed = isStashed; onIsStashedChanged(mIsStashed); + + cancelTimeoutIfExists(); } @Override public void onAnimationEnd(Animator animation) { mAnimator = null; + + if (!mIsStashed) { + tryStartTaskbarTimeout(); + } } }); } @@ -779,6 +811,54 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed); } + /** + * Cancels a timeout if any exists. + */ + public void cancelTimeoutIfExists() { + if (mTimeoutAlarm.alarmPending()) { + mTimeoutAlarm.cancelAlarm(); + } + } + + /** + * Updates the status of the taskbar timeout. + * @param isAutohideSuspended If true, cancels any existing timeout + * If false, attempts to re/start the timeout + */ + public void updateTaskbarTimeout(boolean isAutohideSuspended) { + if (!DisplayController.isTransientTaskbar(mActivity)) { + return; + } + if (isAutohideSuspended) { + cancelTimeoutIfExists(); + } else { + tryStartTaskbarTimeout(); + } + } + + /** + * Attempts to start timer to auto hide the taskbar based on time. + */ + public void tryStartTaskbarTimeout() { + if (!DisplayController.isTransientTaskbar(mActivity)) { + return; + } + if (mIsStashed) { + return; + } + cancelTimeoutIfExists(); + + mTimeoutAlarm.setOnAlarmListener(this::onTaskbarTimeout); + mTimeoutAlarm.setAlarm(NO_TOUCH_TIMEOUT_TO_STASH_MS); + } + + private void onTaskbarTimeout(Alarm alarm) { + if (mControllers.taskbarAutohideSuspendController.isSuspended()) { + return; + } + updateAndAnimateTransientTaskbar(true); + } + @Override public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarStashController:"); @@ -794,17 +874,18 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } private static String getStateString(int flags) { - StringJoiner str = new StringJoiner("|"); - appendFlag(str, flags, FLAGS_IN_APP, "FLAG_IN_APP"); - appendFlag(str, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL"); - appendFlag(str, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED"); - appendFlag(str, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY"); - appendFlag(str, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP"); - appendFlag(str, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME"); - appendFlag(str, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE"); - appendFlag(str, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_APP_ALL_APPS"); - appendFlag(str, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP"); - return str.toString(); + StringJoiner sj = new StringJoiner("|"); + appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_PINNED, "FLAG_STASHED_IN_APP_PINNED"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME"); + appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE"); + appendFlag(sj, flags, FLAG_STASHED_IN_TASKBAR_ALL_APPS, "FLAG_STASHED_IN_TASKBAR_ALL_APPS"); + appendFlag(sj, flags, FLAG_IN_SETUP, "FLAG_IN_SETUP"); + appendFlag(sj, flags, FLAG_STASHED_IN_APP_AUTO, "FLAG_STASHED_IN_APP_AUTO"); + return sj.toString(); } private class StatePropertyHolder { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 9ec8cfeffa..2294306ac3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar; +import android.view.MotionEvent; import android.view.View; import androidx.annotation.CallSuper; @@ -104,6 +105,15 @@ public class TaskbarUIController { return mControllers.taskbarStashController.isStashed(); } + /* + * @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 mControllers.taskbarViewController.isEventOverAnyItem(ev) + || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev); + } + @CallSuper protected void dumpLogs(String prefix, PrintWriter pw) { pw.println(String.format( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index c754a59a06..bdcc4295fe 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -380,13 +380,23 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar return super.dispatchTouchEvent(ev); } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + mControllerCallbacks.onInterceptTouchEvent(ev); + return super.onInterceptTouchEvent(ev); + } + @Override public boolean onTouchEvent(MotionEvent event) { if (!mTouchEnabled) { return true; } - if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) { - // Don't allow long pressing between icons, or above/below them. + if (mIconLayoutBounds.left <= event.getX() + && event.getX() <= mIconLayoutBounds.right + && !DisplayController.isTransientTaskbar(mActivityContext)) { + // Don't allow long pressing between icons, or above/below them + // unless its transient taskbar. + mControllerCallbacks.clearTouchInProgress(); return true; } if (mControllerCallbacks.onTouchEvent(event)) { @@ -403,6 +413,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar public void setTouchesEnabled(boolean touchEnabled) { this.mTouchEnabled = touchEnabled; + mControllerCallbacks.clearTouchInProgress(); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index f0277a4d08..2bc609eaf3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -22,6 +22,8 @@ import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP; import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; import static com.android.quickstep.AnimatedFloat.VALUE; import android.annotation.NonNull; @@ -32,6 +34,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; +import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import androidx.core.view.OneShotPreDrawListener; @@ -48,6 +51,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.ThemedIconDrawable; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.HorizontalInsettableView; import com.android.launcher3.util.ItemInfoMatcher; @@ -97,6 +101,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private final TaskbarModelCallbacks mModelCallbacks; + // Captures swipe down action to close transient taskbar. + protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector; + // Initialized in init. private TaskbarControllers mControllers; @@ -119,6 +126,31 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar mTaskbarBottomMargin = DisplayController.isTransientTaskbar(activity) ? activity.getResources().getDimensionPixelSize(R.dimen.transient_taskbar_margin) : 0; + + if (DisplayController.isTransientTaskbar(mActivity)) { + mSwipeDownDetector = new SingleAxisSwipeDetector(activity, + new SingleAxisSwipeDetector.Listener() { + private float mLastDisplacement; + + @Override + public boolean onDrag(float displacement) { + mLastDisplacement = displacement; + return false; + } + + @Override + public void onDragEnd(float velocity) { + if (mLastDisplacement > 0) { + mControllers.taskbarStashController + .updateAndAnimateTransientTaskbar(true); + } + } + + @Override + public void onDragStart(boolean start, float startDisplacement) {} + }, VERTICAL); + mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false); + } } public void init(TaskbarControllers controllers) { @@ -436,6 +468,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private float mDownX, mDownY; private boolean mCanceledStashHint; + private boolean mTouchInProgress; + public View.OnClickListener getIconOnClickListener() { return mActivity.getItemOnClickListener(); } @@ -463,38 +497,76 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar .updateAndAnimateIsManuallyStashedInApp(true); } + /** + * Simply listens to all intercept touch events passed to TaskbarView. + */ + public void onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mTouchInProgress = true; + } + + if (mTouchInProgress && mSwipeDownDetector != null) { + mSwipeDownDetector.onTouchEvent(ev); + } + + if (ev.getAction() == MotionEvent.ACTION_UP + || ev.getAction() == MotionEvent.ACTION_CANCEL) { + clearTouchInProgress(); + } + } + /** * 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) { + boolean shouldConsumeTouch = false; + boolean clearTouchInProgress = false; + final float x = motionEvent.getRawX(); final float y = motionEvent.getRawY(); switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: + mTouchInProgress = true; mDownX = x; mDownY = y; mControllers.taskbarStashController.startStashHint(/* animateForward = */ true); mCanceledStashHint = false; break; case MotionEvent.ACTION_MOVE: - if (!mCanceledStashHint + if (mTouchInProgress + && !mCanceledStashHint && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) { mControllers.taskbarStashController.startStashHint( /* animateForward= */ false); mCanceledStashHint = true; - return true; + shouldConsumeTouch = true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (!mCanceledStashHint) { + if (mTouchInProgress && !mCanceledStashHint) { mControllers.taskbarStashController.startStashHint( /* animateForward= */ false); } + clearTouchInProgress = true; break; } - return false; + + if (mTouchInProgress && mSwipeDownDetector != null) { + mSwipeDownDetector.onTouchEvent(motionEvent); + } + if (clearTouchInProgress) { + clearTouchInProgress(); + } + return shouldConsumeTouch; + } + + /** + * Ensures that we do not pass any more touch events to the SwipeDetector. + */ + public void clearTouchInProgress() { + mTouchInProgress = false; } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java index 3785de4ecd..1430492401 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java @@ -15,9 +15,14 @@ */ package com.android.quickstep.inputconsumers; +import static android.view.MotionEvent.INVALID_POINTER_ID; + import static com.android.launcher3.Utilities.squaredHypot; +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING; import android.content.Context; +import android.content.res.Resources; +import android.graphics.PointF; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; @@ -25,6 +30,7 @@ import android.view.MotionEvent; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.TaskbarActivityContext; +import com.android.launcher3.util.DisplayController; import com.android.quickstep.InputConsumer; import com.android.systemui.shared.system.InputMonitorCompat; @@ -37,20 +43,32 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { private final GestureDetector mLongPressDetector; private final float mSquaredTouchSlop; - - private float mDownX, mDownY; + private float mLongPressDownX, mLongPressDownY; private boolean mCanceledUnstashHint; private final float mUnstashArea; private final float mScreenWidth; + private final int mTaskbarThreshold; + private boolean mHasPassedTaskbarThreshold; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + private int mActivePointerId = INVALID_POINTER_ID; + + private final boolean mIsTransientTaskbar; + public TaskbarStashInputConsumer(Context context, InputConsumer delegate, InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; mSquaredTouchSlop = Utilities.squaredTouchSlop(context); mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx; - mUnstashArea = context.getResources() - .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); + + Resources res = context.getResources(); + mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); + mTaskbarThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold); + + mIsTransientTaskbar = DisplayController.isTransientTaskbar(context); mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override @@ -76,28 +94,71 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { final float y = ev.getRawY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + + mHasPassedTaskbarThreshold = false; + mTaskbarActivityContext.setAutohideSuspendFlag( + FLAG_AUTOHIDE_SUSPEND_TOUCHING, true); if (isInArea(x)) { - mDownX = x; - mDownY = y; - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ true); - mCanceledUnstashHint = false; + if (!mIsTransientTaskbar) { + mLongPressDownX = x; + mLongPressDownY = y; + mTaskbarActivityContext.startTaskbarUnstashHint( + /* animateForward = */ true); + mCanceledUnstashHint = false; + } + } + break; + case MotionEvent.ACTION_POINTER_UP: + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); } break; case MotionEvent.ACTION_MOVE: - if (!mCanceledUnstashHint - && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) { + if (!mIsTransientTaskbar + && !mCanceledUnstashHint + && squaredHypot(mLongPressDownX - x, mLongPressDownY - y) + > mSquaredTouchSlop) { mTaskbarActivityContext.startTaskbarUnstashHint( /* animateForward = */ false); mCanceledUnstashHint = true; } + + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + float displacementY = mLastPos.y - mDownPos.y; + float verticalDist = Math.abs(displacementY); + boolean passedTaskbarThreshold = verticalDist >= mTaskbarThreshold; + + if (!mHasPassedTaskbarThreshold + && passedTaskbarThreshold + && mIsTransientTaskbar) { + mHasPassedTaskbarThreshold = true; + + mTaskbarActivityContext.onSwipeToUnstashTaskbar(); + } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (!mCanceledUnstashHint) { + if (!mIsTransientTaskbar && !mCanceledUnstashHint) { mTaskbarActivityContext.startTaskbarUnstashHint( /* animateForward = */ false); } + mTaskbarActivityContext.setAutohideSuspendFlag( + FLAG_AUTOHIDE_SUSPEND_TOUCHING, false); + mHasPassedTaskbarThreshold = false; break; } } @@ -111,7 +172,9 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { } private void onLongPressDetected(MotionEvent motionEvent) { - if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) { + if (mTaskbarActivityContext != null + && isInArea(motionEvent.getRawX()) + && !mIsTransientTaskbar) { boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar(); if (taskBarPressed) { setActive(motionEvent);