| /* |
| * Copyright (C) 2023 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.bubbles; |
| |
| import android.annotation.SuppressLint; |
| import android.graphics.PointF; |
| import android.view.MotionEvent; |
| import android.view.View; |
| |
| import androidx.annotation.NonNull; |
| |
| import com.android.launcher3.taskbar.TaskbarActivityContext; |
| import com.android.wm.shell.common.bubbles.RelativeTouchListener; |
| |
| /** |
| * Controls bubble bar drag to dismiss interaction. |
| * Interacts with {@link BubbleDismissController}, used by {@link BubbleBarViewController}. |
| * Supported interactions: |
| * - Drag a single bubble view into dismiss target to remove it. |
| * - Drag the bubble stack into dismiss target to remove all. |
| * Restores initial position of dragged view if released outside of the dismiss target. |
| */ |
| public class BubbleDragController { |
| private final TaskbarActivityContext mActivity; |
| private BubbleBarViewController mBubbleBarViewController; |
| private BubbleDismissController mBubbleDismissController; |
| |
| public BubbleDragController(TaskbarActivityContext activity) { |
| mActivity = activity; |
| } |
| |
| /** |
| * Initializes dependencies when bubble controllers are created. |
| * Should be careful to only access things that were created in constructors for now, as some |
| * controllers may still be waiting for init(). |
| */ |
| public void init(@NonNull BubbleControllers bubbleControllers) { |
| mBubbleBarViewController = bubbleControllers.bubbleBarViewController; |
| mBubbleDismissController = bubbleControllers.bubbleDismissController; |
| } |
| |
| /** |
| * Setup the bubble view for dragging and attach touch listener to it |
| */ |
| @SuppressLint("ClickableViewAccessibility") |
| public void setupBubbleView(@NonNull BubbleView bubbleView) { |
| // Don't setup dragging for overflow bubble view |
| if (bubbleView.getBubble() == null |
| || !(bubbleView.getBubble() instanceof BubbleBarBubble)) return; |
| bubbleView.setOnTouchListener(new BaseDragListener() { |
| @Override |
| protected void onDragStart() { |
| super.onDragStart(); |
| mBubbleBarViewController.onDragStart(bubbleView); |
| } |
| |
| @Override |
| protected void onDragEnd() { |
| super.onDragEnd(); |
| mBubbleBarViewController.onDragEnd(bubbleView); |
| } |
| }); |
| } |
| |
| /** |
| * Setup the bubble bar view for dragging and attach touch listener to it |
| */ |
| @SuppressLint("ClickableViewAccessibility") |
| public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) { |
| PointF initialRelativePivot = new PointF(); |
| bubbleBarView.setOnTouchListener(new BaseDragListener() { |
| @Override |
| public boolean onDown(@NonNull View view, @NonNull MotionEvent event) { |
| if (bubbleBarView.isExpanded()) return false; |
| // Setup dragging only when bubble bar is collapsed |
| return super.onDown(view, event); |
| } |
| |
| @Override |
| protected void onDragStart() { |
| super.onDragStart(); |
| initialRelativePivot.set(bubbleBarView.getRelativePivotX(), |
| bubbleBarView.getRelativePivotY()); |
| bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f); |
| } |
| |
| @Override |
| protected void onDragEnd() { |
| super.onDragEnd(); |
| bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y); |
| } |
| }); |
| } |
| |
| /** |
| * Base drag listener for handling a single bubble view or bubble bar view dragging. |
| * Controls dragging interaction and interacts with {@link BubbleDismissController} |
| * to coordinate dismiss view presentation. |
| * Lifecycle methods can be overridden do add extra setup/clean up steps |
| */ |
| private class BaseDragListener extends RelativeTouchListener { |
| private boolean mHandling; |
| private boolean mDragging; |
| |
| @Override |
| public boolean onDown(@NonNull View view, @NonNull MotionEvent event) { |
| mHandling = true; |
| mActivity.setTaskbarWindowFullscreen(true); |
| mBubbleDismissController.setupDismissView(view); |
| mBubbleDismissController.handleTouchEvent(event); |
| return true; |
| } |
| |
| @Override |
| public void onMove(@NonNull View view, @NonNull MotionEvent event, float viewInitialX, |
| float viewInitialY, float dx, float dy) { |
| if (!mHandling) return; |
| if (!mDragging) { |
| // Start dragging |
| mDragging = true; |
| onDragStart(); |
| } |
| if (!mBubbleDismissController.handleTouchEvent(event)) { |
| // Drag the view if not processed by dismiss controller |
| view.setTranslationX(viewInitialX + dx); |
| view.setTranslationY(viewInitialY + dy); |
| } |
| } |
| |
| @Override |
| public void onUp(@NonNull View view, @NonNull MotionEvent event, float viewInitialX, |
| float viewInitialY, float dx, float dy, float velX, float velY) { |
| onComplete(view, event, viewInitialX, viewInitialY); |
| } |
| |
| @Override |
| public void onCancel(@NonNull View view, @NonNull MotionEvent event, float viewInitialX, |
| float viewInitialY, float dx, float dy) { |
| onComplete(view, event, viewInitialX, viewInitialY); |
| } |
| |
| /** |
| * Prepares dismiss view for dragging. |
| * This method can be overridden to add extra setup on drag start |
| */ |
| protected void onDragStart() { |
| mBubbleDismissController.showDismissView(); |
| } |
| |
| /** |
| * Cleans up dismiss view after dragging. |
| * This method can be overridden to add extra setup on drag end |
| */ |
| protected void onDragEnd() { |
| mBubbleDismissController.hideDismissView(); |
| } |
| |
| /** |
| * Complete drag handling and clean up dependencies |
| */ |
| private void onComplete(@NonNull View view, @NonNull MotionEvent event, |
| float viewInitialX, float viewInitialY) { |
| if (!mHandling) return; |
| if (mDragging) { |
| // Stop dragging |
| mDragging = false; |
| view.setTranslationX(viewInitialX); |
| view.setTranslationY(viewInitialY); |
| onDragEnd(); |
| } |
| mBubbleDismissController.handleTouchEvent(event); |
| mActivity.setTaskbarWindowFullscreen(false); |
| mHandling = false; |
| } |
| } |
| } |