Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 528db439 authored by Mady Mellor's avatar Mady Mellor
Browse files

Collapse bubbles when drag and drop is initiated

Dragging a notification into split screen would fail if bubbles
were expanded while performing this action. I think it makes sense
to collapse the stack if its open when drag and drop for an app
starts.

Bug: 223909931
Test: atest DragAndDropControllerTest
Test: manual - have bubbles expanded
             - swipe down the shade, long press on a notification
               and drag it
             => bubbles are collapsed and the notif is able to be
                put into split screen.
Change-Id: Ib5160b3d772452f1cdd704926887bd38a5fd1ef1
parent 8e157c49
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -214,6 +215,8 @@ public class BubbleController {

    /** One handed mode controller to register transition listener. */
    private Optional<OneHandedController> mOneHandedOptional;
    /** Drag and drop controller to register listener for onDragStarted. */
    private DragAndDropController mDragAndDropController;

    /**
     * Creates an instance of the BubbleController.
@@ -230,6 +233,7 @@ public class BubbleController {
            ShellTaskOrganizer organizer,
            DisplayController displayController,
            Optional<OneHandedController> oneHandedOptional,
            DragAndDropController dragAndDropController,
            ShellExecutor mainExecutor,
            Handler mainHandler,
            TaskViewTransitions taskViewTransitions,
@@ -241,8 +245,8 @@ public class BubbleController {
                new BubbleDataRepository(context, launcherApps, mainExecutor),
                statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
                logger, taskStackListener, organizer, positioner, displayController,
                oneHandedOptional, mainExecutor, mainHandler, taskViewTransitions,
                syncQueue);
                oneHandedOptional, dragAndDropController, mainExecutor, mainHandler,
                taskViewTransitions, syncQueue);
    }

    /**
@@ -264,6 +268,7 @@ public class BubbleController {
            BubblePositioner positioner,
            DisplayController displayController,
            Optional<OneHandedController> oneHandedOptional,
            DragAndDropController dragAndDropController,
            ShellExecutor mainExecutor,
            Handler mainHandler,
            TaskViewTransitions taskViewTransitions,
@@ -293,6 +298,7 @@ public class BubbleController {
        mDisplayController = displayController;
        mTaskViewTransitions = taskViewTransitions;
        mOneHandedOptional = oneHandedOptional;
        mDragAndDropController = dragAndDropController;
        mSyncQueue = syncQueue;
    }

@@ -433,6 +439,7 @@ public class BubbleController {
                });

        mOneHandedOptional.ifPresent(this::registerOneHandedState);
        mDragAndDropController.addListener(this::collapseStack);
    }

    @VisibleForTesting
@@ -884,7 +891,6 @@ public class BubbleController {
        return mBubbleData.isExpanded();
    }

    @VisibleForTesting
    public void collapseStack() {
        mBubbleData.setExpanded(false /* expanded */);
    }
+3 −1
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
@@ -108,6 +109,7 @@ public class WMShellModule {
            ShellTaskOrganizer organizer,
            DisplayController displayController,
            @DynamicOverride Optional<OneHandedController> oneHandedOptional,
            DragAndDropController dragAndDropController,
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellMainThread Handler mainHandler,
            TaskViewTransitions taskViewTransitions,
@@ -116,7 +118,7 @@ public class WMShellModule {
                floatingContentCoordinator, statusBarService, windowManager,
                windowManagerShellWrapper, launcherApps, taskStackListener,
                uiEventLogger, organizer, displayController, oneHandedOptional,
                mainExecutor, mainHandler, taskViewTransitions, syncQueue);
                dragAndDropController, mainExecutor, mainHandler, taskViewTransitions, syncQueue);
    }

    //
+37 −6
Original line number Diff line number Diff line
@@ -35,9 +35,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.ClipDescription;
import android.content.Context;
import android.content.res.Configuration;
@@ -52,17 +49,19 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.annotation.VisibleForTesting;

import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;

import java.util.ArrayList;
import java.util.Optional;

/**
@@ -80,10 +79,19 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
    private SplitScreenController mSplitScreen;
    private ShellExecutor mMainExecutor;
    private DragAndDropImpl mImpl;
    private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();

    private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();

    /**
     * Listener called during drag events, currently just onDragStarted.
     */
    public interface DragAndDropListener {
        /** Called when a drag has started. */
        void onDragStarted();
    }

    public DragAndDropController(Context context, DisplayController displayController,
            UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) {
        mContext = context;
@@ -103,6 +111,22 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
        mDisplayController.addDisplayWindowListener(this);
    }

    /** Adds a listener to be notified of drag and drop events. */
    public void addListener(DragAndDropListener listener) {
        mListeners.add(listener);
    }

    /** Removes a drag and drop listener. */
    public void removeListener(DragAndDropListener listener) {
        mListeners.remove(listener);
    }

    private void notifyListeners() {
        for (int i = 0; i < mListeners.size(); i++) {
            mListeners.get(i).onDragStarted();
        }
    }

    @Override
    public void onDisplayAdded(int displayId) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId);
@@ -137,13 +161,19 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
                new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        try {
            wm.addView(rootView, layoutParams);
            mDisplayDropTargets.put(displayId,
                    new PerDisplay(displayId, context, wm, rootView, dragLayout));
            addDisplayDropTarget(displayId, context, wm, rootView, dragLayout);
        } catch (WindowManager.InvalidDisplayException e) {
            Slog.w(TAG, "Unable to add view for display id: " + displayId);
        }
    }

    @VisibleForTesting
    void addDisplayDropTarget(int displayId, Context context, WindowManager wm,
            FrameLayout rootView, DragLayout dragLayout) {
        mDisplayDropTargets.put(displayId,
                new PerDisplay(displayId, context, wm, rootView, dragLayout));
    }

    @Override
    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display changed: %d", displayId);
@@ -206,6 +236,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
                pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId),
                        event.getClipData(), loggerSessionId);
                setDropTargetWindowVisibility(pd, View.VISIBLE);
                notifyListeners();
                break;
            case ACTION_DRAG_ENTERED:
                pd.dragLayout.show();
+61 −0
Original line number Diff line number Diff line
@@ -16,15 +16,28 @@

package com.android.wm.shell.draganddrop;

import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DragEvent.ACTION_DRAG_STARTED;

import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.view.Display;
import android.view.DragEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -33,6 +46,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.splitscreen.SplitScreenController;

import org.junit.Before;
import org.junit.Test;
@@ -40,6 +54,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Optional;

/**
 * Tests for the drag and drop controller.
 */
@@ -56,6 +72,9 @@ public class DragAndDropControllerTest {
    @Mock
    private UiEventLogger mUiEventLogger;

    @Mock
    private DragAndDropController.DragAndDropListener mDragAndDropListener;

    private DragAndDropController mController;

    @Before
@@ -63,6 +82,7 @@ public class DragAndDropControllerTest {
        MockitoAnnotations.initMocks(this);
        mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger,
                mock(IconProvider.class), mock(ShellExecutor.class));
        mController.initialize(Optional.of(mock(SplitScreenController.class)));
    }

    @Test
@@ -77,4 +97,45 @@ public class DragAndDropControllerTest {
        mController.onDisplayAdded(nonDefaultDisplayId);
        assertFalse(mController.onDrag(dragLayout, mock(DragEvent.class)));
    }

    @Test
    public void testListenerOnDragStarted() {
        final View dragLayout = mock(View.class);
        final Display display = mock(Display.class);
        doReturn(display).when(dragLayout).getDisplay();
        doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();

        final ClipData clipData = createClipData();
        final DragEvent event = mock(DragEvent.class);
        doReturn(ACTION_DRAG_STARTED).when(event).getAction();
        doReturn(clipData).when(event).getClipData();
        doReturn(clipData.getDescription()).when(event).getClipDescription();

        mController.addListener(mDragAndDropListener);

        // Ensure there's a target so that onDrag will execute
        mController.addDisplayDropTarget(0, mContext, mock(WindowManager.class),
                mock(FrameLayout.class), mock(DragLayout.class));

        // Verify the listener is called on a valid drag action.
        mController.onDrag(dragLayout, event);
        verify(mDragAndDropListener, times(1)).onDragStarted();

        // Verify the listener isn't called after removal.
        reset(mDragAndDropListener);
        mController.removeListener(mDragAndDropListener);
        mController.onDrag(dragLayout, event);
        verify(mDragAndDropListener, never()).onDragStarted();
    }

    private ClipData createClipData() {
        ClipDescription clipDescription = new ClipDescription(MIMETYPE_APPLICATION_SHORTCUT,
                new String[] { MIMETYPE_APPLICATION_SHORTCUT });
        Intent i = new Intent();
        i.putExtra(Intent.EXTRA_PACKAGE_NAME, "pkg");
        i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcutId");
        i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
        ClipData.Item item = new ClipData.Item(i);
        return new ClipData(clipDescription, item);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;

import com.google.common.collect.ImmutableList;
@@ -367,6 +368,7 @@ public class BubblesTest extends SysuiTestCase {
                mPositioner,
                mock(DisplayController.class),
                mOneHandedOptional,
                mock(DragAndDropController.class),
                syncExecutor,
                mock(Handler.class),
                mTaskViewTransitions,
Loading