Merge "Convert TaskViewTouchController to Kotlin." into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
4e09250fd5
@@ -23,6 +23,7 @@ import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER
|
||||
|
||||
import static com.android.app.animation.Interpolators.EMPHASIZED;
|
||||
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
|
||||
import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
|
||||
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
|
||||
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
|
||||
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
|
||||
@@ -149,7 +150,9 @@ import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTou
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
|
||||
import com.android.launcher3.util.ActivityOptionsWrapper;
|
||||
@@ -265,6 +268,25 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
|
||||
private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChanged;
|
||||
|
||||
private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
|
||||
new TaskViewRecentsTouchContext() {
|
||||
@Override
|
||||
public boolean isRecentsInteractive() {
|
||||
return isInState(OVERVIEW) || isInState(OVERVIEW_MODAL_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecentsModal() {
|
||||
return isInState(OVERVIEW_MODAL_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserControlledAnimationCreated(
|
||||
AnimatorPlaybackController animController) {
|
||||
getStateManager().setCurrentUserControlledAnimation(animController);
|
||||
}
|
||||
};
|
||||
|
||||
public static QuickstepLauncher getLauncher(Context context) {
|
||||
return fromContext(context);
|
||||
}
|
||||
@@ -665,7 +687,11 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
list.add(new StatusBarTouchController(this));
|
||||
}
|
||||
|
||||
list.add(new LauncherTaskViewController(this));
|
||||
if (enableExpressiveDismissTaskMotion()) {
|
||||
list.add(new TaskViewTouchController<>(this, mTaskViewRecentsTouchContext));
|
||||
} else {
|
||||
list.add(new TaskViewTouchControllerDeprecated<>(this, mTaskViewRecentsTouchContext));
|
||||
}
|
||||
return list.toArray(new TouchController[list.size()]);
|
||||
}
|
||||
|
||||
@@ -1442,29 +1468,6 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
mBubbleBarLocation = bubbleBarLocation;
|
||||
}
|
||||
|
||||
private static final class LauncherTaskViewController extends
|
||||
TaskViewTouchController<QuickstepLauncher> {
|
||||
|
||||
LauncherTaskViewController(QuickstepLauncher activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRecentsInteractive() {
|
||||
return mContainer.isInState(OVERVIEW) || mContainer.isInState(OVERVIEW_MODAL_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRecentsModal() {
|
||||
return mContainer.isInState(OVERVIEW_MODAL_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
|
||||
mContainer.getStateManager().setCurrentUserControlledAnimation(animController);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
super.dump(prefix, fd, writer, args);
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.uioverrides.touchcontrollers;
|
||||
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
|
||||
/** Interface providing context about the RecentsView state to a {@link TaskViewTouchController}. */
|
||||
public interface TaskViewRecentsTouchContext {
|
||||
/** Returns whether Recents is interactive for touch. */
|
||||
boolean isRecentsInteractive();
|
||||
|
||||
/** Returns if Recents is showing a single task in a modal way. */
|
||||
boolean isRecentsModal();
|
||||
|
||||
/** Runs when a user controlled animation is created. */
|
||||
default void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
|
||||
}
|
||||
}
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.uioverrides.touchcontrollers
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.os.VibrationEffect
|
||||
import android.view.MotionEvent
|
||||
import android.view.animation.Interpolator
|
||||
import com.android.app.animation.Interpolators
|
||||
import com.android.launcher3.AbstractFloatingView
|
||||
import com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS
|
||||
import com.android.launcher3.LauncherAnimUtils.blockedFlingDurationFactor
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.Utilities
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController
|
||||
import com.android.launcher3.anim.PendingAnimation
|
||||
import com.android.launcher3.touch.BaseSwipeDetector
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector
|
||||
import com.android.launcher3.util.DisplayController
|
||||
import com.android.launcher3.util.FlingBlockCheck
|
||||
import com.android.launcher3.util.TouchController
|
||||
import com.android.launcher3.util.VibratorWrapper
|
||||
import com.android.quickstep.util.VibrationConstants
|
||||
import com.android.quickstep.views.RecentsView
|
||||
import com.android.quickstep.views.RecentsViewContainer
|
||||
import com.android.quickstep.views.TaskView
|
||||
import kotlin.math.abs
|
||||
|
||||
/** Touch controller for handling task view card swipes */
|
||||
class TaskViewTouchController<CONTAINER>(
|
||||
private val container: CONTAINER,
|
||||
private val taskViewRecentsTouchContext: TaskViewRecentsTouchContext,
|
||||
) : AnimatorListenerAdapter(), TouchController, SingleAxisSwipeDetector.Listener where
|
||||
CONTAINER : Context,
|
||||
CONTAINER : RecentsViewContainer {
|
||||
private val recentsView: RecentsView<*, *> = container.getOverviewPanel()
|
||||
private val detector: SingleAxisSwipeDetector =
|
||||
SingleAxisSwipeDetector(
|
||||
container as Context,
|
||||
this,
|
||||
recentsView.pagedOrientationHandler.upDownSwipeDirection,
|
||||
)
|
||||
private val tempRect = Rect()
|
||||
private val isRtl = Utilities.isRtl(container.resources)
|
||||
private val flingBlockCheck = FlingBlockCheck()
|
||||
|
||||
private var currentAnimation: AnimatorPlaybackController? = null
|
||||
private var currentAnimationIsGoingUp = false
|
||||
private var allowGoingUp = false
|
||||
private var allowGoingDown = false
|
||||
private var noIntercept = false
|
||||
private var displacementShift = 0f
|
||||
private var progressMultiplier = 0f
|
||||
private var endDisplacement = 0f
|
||||
private var draggingEnabled = true
|
||||
private var overrideVelocity: Float? = null
|
||||
private var taskBeingDragged: TaskView? = null
|
||||
private var isDismissHapticRunning = false
|
||||
|
||||
private fun canInterceptTouch(ev: MotionEvent): Boolean {
|
||||
val currentAnimation = currentAnimation
|
||||
return when {
|
||||
(ev.edgeFlags and Utilities.EDGE_NAV_BAR) != 0 -> {
|
||||
// Don't intercept swipes on the nav bar, as user might be trying to go home
|
||||
// during a task dismiss animation.
|
||||
currentAnimation?.animationPlayer?.end()
|
||||
false
|
||||
}
|
||||
currentAnimation != null -> {
|
||||
currentAnimation.forceFinishIfCloseToEnd()
|
||||
true
|
||||
}
|
||||
AbstractFloatingView.getTopOpenViewWithType(
|
||||
container,
|
||||
AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT,
|
||||
) != null -> false
|
||||
else -> taskViewRecentsTouchContext.isRecentsInteractive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animation: Animator) {
|
||||
if (animation === currentAnimation?.target) {
|
||||
clearState()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (
|
||||
(ev.action == MotionEvent.ACTION_UP || ev.action == MotionEvent.ACTION_CANCEL) &&
|
||||
currentAnimation == null
|
||||
) {
|
||||
clearState()
|
||||
}
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
// Disable swiping up and down if the task overlay is modal.
|
||||
if (taskViewRecentsTouchContext.isRecentsModal) {
|
||||
noIntercept = true
|
||||
return false
|
||||
}
|
||||
noIntercept = !canInterceptTouch(ev)
|
||||
if (noIntercept) {
|
||||
return false
|
||||
}
|
||||
// Now figure out which direction scroll events the controller will start
|
||||
// calling the callbacks.
|
||||
var directionsToDetectScroll = 0
|
||||
var ignoreSlopWhenSettling = false
|
||||
if (currentAnimation != null) {
|
||||
directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH
|
||||
ignoreSlopWhenSettling = true
|
||||
} else {
|
||||
taskBeingDragged = null
|
||||
recentsView.taskViews.forEach { taskView ->
|
||||
if (
|
||||
recentsView.isTaskViewVisible(taskView) &&
|
||||
container.dragLayer.isEventOverView(taskView, ev)
|
||||
) {
|
||||
taskBeingDragged = taskView
|
||||
val upDirection = recentsView.pagedOrientationHandler.getUpDirection(isRtl)
|
||||
|
||||
// The task can be dragged up to dismiss it
|
||||
allowGoingUp = true
|
||||
|
||||
// The task can be dragged down to open it if:
|
||||
// - It's the current page
|
||||
// - We support gestures to enter overview
|
||||
// - It's the focused task if in grid view
|
||||
// - The task is snapped
|
||||
allowGoingDown =
|
||||
taskView === recentsView.currentPageTaskView &&
|
||||
DisplayController.getNavigationMode(container).hasGestures &&
|
||||
(!recentsView.showAsGrid() || taskView.isLargeTile) &&
|
||||
recentsView.isTaskInExpectedScrollPosition(taskView)
|
||||
|
||||
directionsToDetectScroll =
|
||||
if (allowGoingDown) SingleAxisSwipeDetector.DIRECTION_BOTH
|
||||
else upDirection
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
if (taskBeingDragged == null) {
|
||||
noIntercept = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
detector.setDetectableScrollConditions(directionsToDetectScroll, ignoreSlopWhenSettling)
|
||||
}
|
||||
if (noIntercept) {
|
||||
return false
|
||||
}
|
||||
onControllerTouchEvent(ev)
|
||||
return detector.isDraggingOrSettling
|
||||
}
|
||||
|
||||
override fun onControllerTouchEvent(ev: MotionEvent): Boolean = detector.onTouchEvent(ev)
|
||||
|
||||
private fun reInitAnimationController(goingUp: Boolean) {
|
||||
if (currentAnimation != null && currentAnimationIsGoingUp == goingUp) {
|
||||
// No need to init
|
||||
return
|
||||
}
|
||||
if ((goingUp && !allowGoingUp) || (!goingUp && !allowGoingDown)) {
|
||||
// Trying to re-init in an unsupported direction.
|
||||
return
|
||||
}
|
||||
val taskBeingDragged = taskBeingDragged ?: return
|
||||
currentAnimation?.setPlayFraction(0f)
|
||||
currentAnimation?.target?.removeListener(this)
|
||||
currentAnimation?.dispatchOnCancel()
|
||||
|
||||
val orientationHandler = recentsView.pagedOrientationHandler
|
||||
currentAnimationIsGoingUp = goingUp
|
||||
val dl = container.dragLayer
|
||||
val secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl)
|
||||
val maxDuration = 2L * secondaryLayerDimension
|
||||
val verticalFactor = orientationHandler.getTaskDragDisplacementFactor(isRtl)
|
||||
val secondaryTaskDimension = orientationHandler.getSecondaryDimension(taskBeingDragged)
|
||||
// The interpolator controlling the most prominent visual movement. We use this to determine
|
||||
// whether we passed SUCCESS_TRANSITION_PROGRESS.
|
||||
val currentInterpolator: Interpolator
|
||||
val pa: PendingAnimation
|
||||
if (goingUp) {
|
||||
currentInterpolator = Interpolators.LINEAR
|
||||
pa = PendingAnimation(maxDuration)
|
||||
recentsView.createTaskDismissAnimation(
|
||||
pa,
|
||||
taskBeingDragged,
|
||||
true, /* animateTaskView */
|
||||
true, /* removeTask */
|
||||
maxDuration,
|
||||
false, /* dismissingForSplitSelection*/
|
||||
)
|
||||
|
||||
endDisplacement = -secondaryTaskDimension.toFloat()
|
||||
} else {
|
||||
currentInterpolator = Interpolators.ZOOM_IN
|
||||
pa =
|
||||
recentsView.createTaskLaunchAnimation(
|
||||
taskBeingDragged,
|
||||
maxDuration,
|
||||
currentInterpolator,
|
||||
)
|
||||
|
||||
// Since the thumbnail is what is filling the screen, based the end displacement on it.
|
||||
taskBeingDragged.getThumbnailBounds(tempRect, /* relativeToDragLayer= */ true)
|
||||
endDisplacement = (secondaryLayerDimension - tempRect.bottom).toFloat()
|
||||
}
|
||||
endDisplacement *= verticalFactor.toFloat()
|
||||
currentAnimation =
|
||||
pa.createPlaybackController().apply {
|
||||
// Setting this interpolator doesn't affect the visual motion, but is used to
|
||||
// determine whether we successfully reached the target state in onDragEnd().
|
||||
target.interpolator = currentInterpolator
|
||||
taskViewRecentsTouchContext.onUserControlledAnimationCreated(this)
|
||||
target.addListener(this@TaskViewTouchController)
|
||||
dispatchOnStart()
|
||||
}
|
||||
progressMultiplier = 1 / endDisplacement
|
||||
}
|
||||
|
||||
override fun onDragStart(start: Boolean, startDisplacement: Float) {
|
||||
if (!draggingEnabled) return
|
||||
val currentAnimation = currentAnimation
|
||||
|
||||
val orientationHandler = recentsView.pagedOrientationHandler
|
||||
if (currentAnimation == null) {
|
||||
reInitAnimationController(orientationHandler.isGoingUp(startDisplacement, isRtl))
|
||||
displacementShift = 0f
|
||||
} else {
|
||||
displacementShift = currentAnimation.progressFraction / progressMultiplier
|
||||
currentAnimation.pause()
|
||||
}
|
||||
flingBlockCheck.unblockFling()
|
||||
overrideVelocity = null
|
||||
}
|
||||
|
||||
override fun onDrag(displacement: Float): Boolean {
|
||||
if (!draggingEnabled) return true
|
||||
val taskBeingDragged = taskBeingDragged ?: return true
|
||||
val currentAnimation = currentAnimation ?: return true
|
||||
|
||||
val orientationHandler = recentsView.pagedOrientationHandler
|
||||
val totalDisplacement = displacement + displacementShift
|
||||
val isGoingUp =
|
||||
if (totalDisplacement == 0f) currentAnimationIsGoingUp
|
||||
else orientationHandler.isGoingUp(totalDisplacement, isRtl)
|
||||
if (isGoingUp != currentAnimationIsGoingUp) {
|
||||
reInitAnimationController(isGoingUp)
|
||||
flingBlockCheck.blockFling()
|
||||
} else {
|
||||
flingBlockCheck.onEvent()
|
||||
}
|
||||
|
||||
if (isGoingUp) {
|
||||
if (currentAnimation.progressFraction < ANIMATION_PROGRESS_FRACTION_MIDPOINT) {
|
||||
// Halve the value when dismissing, as we are animating the drag across the full
|
||||
// length for only the first half of the progress
|
||||
currentAnimation.setPlayFraction(
|
||||
Utilities.boundToRange(totalDisplacement * progressMultiplier / 2, 0f, 1f)
|
||||
)
|
||||
} else {
|
||||
// Set mOverrideVelocity to control task dismiss velocity in onDragEnd
|
||||
var velocityDimenId = R.dimen.default_task_dismiss_drag_velocity
|
||||
if (recentsView.showAsGrid()) {
|
||||
velocityDimenId =
|
||||
if (taskBeingDragged.isLargeTile) {
|
||||
R.dimen.default_task_dismiss_drag_velocity_grid_focus_task
|
||||
} else {
|
||||
R.dimen.default_task_dismiss_drag_velocity_grid
|
||||
}
|
||||
}
|
||||
overrideVelocity = -taskBeingDragged.resources.getDimension(velocityDimenId)
|
||||
|
||||
// Once halfway through task dismissal interpolation, switch from reversible
|
||||
// dragging-task animation to playing the remaining task translation animations,
|
||||
// while this is in progress disable dragging.
|
||||
draggingEnabled = false
|
||||
}
|
||||
} else {
|
||||
currentAnimation.setPlayFraction(
|
||||
Utilities.boundToRange(totalDisplacement * progressMultiplier, 0f, 1f)
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDragEnd(velocity: Float) {
|
||||
val taskBeingDragged = taskBeingDragged ?: return
|
||||
val currentAnimation = currentAnimation ?: return
|
||||
|
||||
// Limit velocity, as very large scalar values make animations play too quickly
|
||||
val maxTaskDismissDragVelocity =
|
||||
taskBeingDragged.resources.getDimension(R.dimen.max_task_dismiss_drag_velocity)
|
||||
val endVelocity =
|
||||
Utilities.boundToRange(
|
||||
overrideVelocity ?: velocity,
|
||||
-maxTaskDismissDragVelocity,
|
||||
maxTaskDismissDragVelocity,
|
||||
)
|
||||
overrideVelocity = null
|
||||
|
||||
var fling = draggingEnabled && detector.isFling(endVelocity)
|
||||
val goingToEnd: Boolean
|
||||
val blockedFling = fling && flingBlockCheck.isBlocked
|
||||
if (blockedFling) {
|
||||
fling = false
|
||||
}
|
||||
val orientationHandler = recentsView.pagedOrientationHandler
|
||||
val goingUp = orientationHandler.isGoingUp(endVelocity, isRtl)
|
||||
val progress = currentAnimation.progressFraction
|
||||
val interpolatedProgress = currentAnimation.interpolatedProgress
|
||||
goingToEnd =
|
||||
if (fling) {
|
||||
goingUp == currentAnimationIsGoingUp
|
||||
} else {
|
||||
interpolatedProgress > SUCCESS_TRANSITION_PROGRESS
|
||||
}
|
||||
var animationDuration =
|
||||
BaseSwipeDetector.calculateDuration(
|
||||
endVelocity,
|
||||
if (goingToEnd) (1 - progress) else progress,
|
||||
)
|
||||
if (blockedFling && !goingToEnd) {
|
||||
animationDuration *= blockedFlingDurationFactor(endVelocity).toLong()
|
||||
}
|
||||
// Due to very high or low velocity dismissals, animation durations can be inconsistently
|
||||
// long or short. Bound the duration for animation of task translations for a more
|
||||
// standardized feel.
|
||||
animationDuration =
|
||||
Utilities.boundToRange(
|
||||
animationDuration,
|
||||
MIN_TASK_DISMISS_ANIMATION_DURATION,
|
||||
MAX_TASK_DISMISS_ANIMATION_DURATION,
|
||||
)
|
||||
|
||||
currentAnimation.setEndAction { this.clearState() }
|
||||
currentAnimation.startWithVelocity(
|
||||
container,
|
||||
goingToEnd,
|
||||
abs(endVelocity.toDouble()).toFloat(),
|
||||
endDisplacement,
|
||||
animationDuration,
|
||||
)
|
||||
if (goingUp && goingToEnd && !isDismissHapticRunning) {
|
||||
VibratorWrapper.INSTANCE.get(container)
|
||||
.vibrate(
|
||||
TASK_DISMISS_VIBRATION_PRIMITIVE,
|
||||
TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE,
|
||||
TASK_DISMISS_VIBRATION_FALLBACK,
|
||||
)
|
||||
isDismissHapticRunning = true
|
||||
}
|
||||
|
||||
draggingEnabled = true
|
||||
}
|
||||
|
||||
private fun clearState() {
|
||||
detector.finishedScrolling()
|
||||
detector.setDetectableScrollConditions(0, false)
|
||||
draggingEnabled = true
|
||||
taskBeingDragged = null
|
||||
currentAnimation = null
|
||||
isDismissHapticRunning = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f
|
||||
private const val MIN_TASK_DISMISS_ANIMATION_DURATION: Long = 300
|
||||
private const val MAX_TASK_DISMISS_ANIMATION_DURATION: Long = 600
|
||||
|
||||
private const val TASK_DISMISS_VIBRATION_PRIMITIVE: Int =
|
||||
VibrationEffect.Composition.PRIMITIVE_TICK
|
||||
private const val TASK_DISMISS_VIBRATION_PRIMITIVE_SCALE: Float = 1f
|
||||
private val TASK_DISMISS_VIBRATION_FALLBACK: VibrationEffect =
|
||||
VibrationConstants.EFFECT_TEXTURE_TICK
|
||||
}
|
||||
}
|
||||
+13
-15
@@ -49,10 +49,13 @@ import com.android.quickstep.views.TaskView;
|
||||
|
||||
/**
|
||||
* Touch controller for handling task view card swipes
|
||||
*
|
||||
* @deprecated This class will be replaced by the new {@link TaskViewTouchController}.
|
||||
*/
|
||||
public abstract class TaskViewTouchController<CONTAINER extends Context & RecentsViewContainer>
|
||||
extends AnimatorListenerAdapter implements TouchController,
|
||||
SingleAxisSwipeDetector.Listener {
|
||||
@Deprecated
|
||||
public class TaskViewTouchControllerDeprecated<
|
||||
CONTAINER extends Context & RecentsViewContainer> extends AnimatorListenerAdapter
|
||||
implements TouchController, SingleAxisSwipeDetector.Listener {
|
||||
|
||||
private static final float ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f;
|
||||
private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
|
||||
@@ -65,6 +68,7 @@ public abstract class TaskViewTouchController<CONTAINER extends Context & Recent
|
||||
VibrationConstants.EFFECT_TEXTURE_TICK;
|
||||
|
||||
protected final CONTAINER mContainer;
|
||||
private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext;
|
||||
private final SingleAxisSwipeDetector mDetector;
|
||||
private final RecentsView<?, ?> mRecentsView;
|
||||
private final Rect mTempRect = new Rect();
|
||||
@@ -88,8 +92,10 @@ public abstract class TaskViewTouchController<CONTAINER extends Context & Recent
|
||||
|
||||
private boolean mIsDismissHapticRunning = false;
|
||||
|
||||
public TaskViewTouchController(CONTAINER container) {
|
||||
public TaskViewTouchControllerDeprecated(CONTAINER container,
|
||||
TaskViewRecentsTouchContext taskViewRecentsTouchContext) {
|
||||
mContainer = container;
|
||||
mTaskViewRecentsTouchContext = taskViewRecentsTouchContext;
|
||||
mRecentsView = container.getOverviewPanel();
|
||||
mIsRtl = Utilities.isRtl(container.getResources());
|
||||
SingleAxisSwipeDetector.Direction dir =
|
||||
@@ -117,15 +123,7 @@ public abstract class TaskViewTouchController<CONTAINER extends Context & Recent
|
||||
mContainer, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) {
|
||||
return false;
|
||||
}
|
||||
return isRecentsInteractive();
|
||||
}
|
||||
|
||||
protected abstract boolean isRecentsInteractive();
|
||||
|
||||
/** Is recents view showing a single task in a modal way. */
|
||||
protected abstract boolean isRecentsModal();
|
||||
|
||||
protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
|
||||
return mTaskViewRecentsTouchContext.isRecentsInteractive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +159,7 @@ public abstract class TaskViewTouchController<CONTAINER extends Context & Recent
|
||||
if (mRecentsView.isTaskViewVisible(taskView) && mContainer.getDragLayer()
|
||||
.isEventOverView(taskView, ev)) {
|
||||
// Disable swiping up and down if the task overlay is modal.
|
||||
if (isRecentsModal()) {
|
||||
if (mTaskViewRecentsTouchContext.isRecentsModal()) {
|
||||
mTaskBeingDragged = null;
|
||||
break;
|
||||
}
|
||||
@@ -259,7 +257,7 @@ public abstract class TaskViewTouchController<CONTAINER extends Context & Recent
|
||||
// Setting this interpolator doesn't affect the visual motion, but is used to determine
|
||||
// whether we successfully reached the target state in onDragEnd().
|
||||
mCurrentAnimation.getTarget().setInterpolator(currentInterpolator);
|
||||
onUserControlledAnimationCreated(mCurrentAnimation);
|
||||
mTaskViewRecentsTouchContext.onUserControlledAnimationCreated(mCurrentAnimation);
|
||||
mCurrentAnimation.getTarget().addListener(this);
|
||||
mCurrentAnimation.dispatchOnStart();
|
||||
mProgressMultiplier = 1 / mEndDisplacement;
|
||||
@@ -15,9 +15,15 @@
|
||||
*/
|
||||
package com.android.quickstep.fallback;
|
||||
|
||||
import static com.android.launcher3.Flags.enableExpressiveDismissTaskMotion;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.statemanager.StatefulContainer;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewRecentsTouchContext;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchControllerDeprecated;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.views.RecentsViewContainer;
|
||||
@@ -25,16 +31,33 @@ import com.android.quickstep.views.RecentsViewContainer;
|
||||
/**
|
||||
* Drag layer for fallback recents activity
|
||||
*/
|
||||
public class RecentsDragLayer<T extends Context & RecentsViewContainer> extends BaseDragLayer<T> {
|
||||
public class RecentsDragLayer<T extends Context & RecentsViewContainer
|
||||
& StatefulContainer<RecentsState>> extends BaseDragLayer<T> {
|
||||
|
||||
private final TaskViewRecentsTouchContext mTaskViewRecentsTouchContext =
|
||||
new TaskViewRecentsTouchContext() {
|
||||
@Override
|
||||
public boolean isRecentsInteractive() {
|
||||
return mContainer.getRootView().hasWindowFocus()
|
||||
|| mContainer.getStateManager().getState().hasLiveTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecentsModal() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public RecentsDragLayer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, 1 /* alphaChannelCount */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateControllers() {
|
||||
mControllers = new TouchController[] {
|
||||
new RecentsTaskController(mContainer),
|
||||
new FallbackNavBarTouchController(mContainer),
|
||||
};
|
||||
mControllers = new TouchController[]{
|
||||
enableExpressiveDismissTaskMotion() ? new TaskViewTouchController<>(mContainer,
|
||||
mTaskViewRecentsTouchContext) : new TaskViewTouchControllerDeprecated<>(
|
||||
mContainer, mTaskViewRecentsTouchContext),
|
||||
new FallbackNavBarTouchController(mContainer)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.quickstep.fallback;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.launcher3.statemanager.StatefulContainer;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
|
||||
import com.android.quickstep.views.RecentsViewContainer;
|
||||
|
||||
public class RecentsTaskController<T extends Context & RecentsViewContainer &
|
||||
StatefulContainer<RecentsState>> extends TaskViewTouchController<T> {
|
||||
public RecentsTaskController(T container) {
|
||||
super(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRecentsInteractive() {
|
||||
return mContainer.getRootView().hasWindowFocus()
|
||||
|| mContainer.getStateManager().getState().hasLiveTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRecentsModal() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user