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

Commit 22bdd224 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix crash when pasting on a root in the sidebar" into main

parents b0e631f3 df2c5854
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ public class FilesJankPerfTest extends JankTestBase {
        final UiDevice device = UiDevice.getInstance(getInstrumentation());
        final Context context = getInstrumentation().getTargetContext();
        final UiAutomation automation = getInstrumentation().getUiAutomation();
        mRootsListBot = new SidebarBot(device, context, BOT_TIMEOUT);
        mRootsListBot = new SidebarBot(device, automation, context, BOT_TIMEOUT);
        mDirListBot = new DirectoryListBot(device, automation, context, BOT_TIMEOUT);

        final Intent intent = new Intent(context, FilesActivity.class);
+7 −2
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import androidx.recyclerview.selection.Selection;

import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.Features;
import com.android.documentsui.base.Shared;
import com.android.documentsui.services.FileOperation;
import com.android.documentsui.services.FileOperationService;
@@ -240,7 +239,13 @@ final class RuntimeDocumentClipper implements DocumentClipper {
            @Nullable ClipData clipData,
            FileOperations.Callback callback) {

        DocumentStack dstStack = new DocumentStack(docStack, destination);
        // In the case where a destination is actually a root, the docStack contains a single value
        // of the root. We can avoid copying and recreating a new `DocumentStack` here as the
        // `docStack` is sufficiently describing the destination.
        DocumentStack dstStack =
                (docStack != null && docStack.size() == 1 && docStack.get(0).equals(destination))
                        ? docStack
                        : new DocumentStack(docStack, destination);
        copyFromClipData(dstStack, clipData, callback);
    }

+53 −2
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static junit.framework.Assert.assertNotNull;

import android.app.UiAutomation;
import android.content.Context;
import android.os.SystemClock;
import android.view.MotionEvent;

import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
@@ -53,7 +55,7 @@ public final class Bots {
    public Bots(UiDevice device, UiAutomation automation, Context context, int timeout) {
        main = new UiBot(device, context, TIMEOUT);
        breadcrumb = new BreadBot(device, context, TIMEOUT);
        roots = new SidebarBot(device, context, TIMEOUT);
        roots = new SidebarBot(device, automation, context, TIMEOUT);
        directory = new DirectoryListBot(device, automation, context, TIMEOUT);
        sort = new SortBot(device, context, TIMEOUT, main);
        keyboard = new KeyboardBot(device, context, TIMEOUT);
@@ -71,9 +73,9 @@ public final class Bots {
     */
    public static abstract class BaseBot {
        public final UiDevice mDevice;
        public final String mTargetPackage;
        final Context mContext;
        final int mTimeout;
        public final String mTargetPackage;

        BaseBot(UiDevice device, Context context, int timeout) {
            mDevice = device;
@@ -84,6 +86,55 @@ public final class Bots {
                            .getTargetContext().getPackageName();
        }

        /**
         * Returns a `MotionEvent` that mocks a right click.
         * There are 2 ways right clicks are intercepted throughout DocumentsUI:
         *   1. Via an onClickListener and thus the actions can simply be one of ACTION_DOWN and
         *      ACTION_UP.
         *   2. Via an onGenericMotionListener and therefore there needs to be 4 actions,
         *      ACTION_DOWN, ACTION_BUTTON_PRESS, ACTION_BUTTON_RELEASE and ACTION_UP.
         */
        protected static MotionEvent getTestRightClickMotionEvent(int action, int x, int y) {
            long eventTime = SystemClock.uptimeMillis();

            MotionEvent.PointerProperties[] pp = {new MotionEvent.PointerProperties()};
            pp[0].clear();
            pp[0].id = 0;
            pp[0].toolType = MotionEvent.TOOL_TYPE_MOUSE;

            MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
            pointerCoords[0].clear();
            pointerCoords[0].x = x;
            pointerCoords[0].y = y;
            pointerCoords[0].pressure = 0;
            pointerCoords[0].size = 1;

            MotionEvent event =
                    MotionEvent.obtain(
                            eventTime,
                            eventTime,
                            action,
                            1, // pointerCount.
                            pp,
                            pointerCoords,
                            0, // metaState.
                            MotionEvent.BUTTON_SECONDARY,
                            1f, // xPrecision.
                            1f, // yPrecision.
                            0, // deviceId.
                            0, // edgeFlags.
                            android.view.InputDevice.SOURCE_MOUSE,
                            0 // flags.
                    );

            if (action == MotionEvent.ACTION_BUTTON_PRESS
                    || action == MotionEvent.ACTION_BUTTON_RELEASE) {
                event.setActionButton(MotionEvent.BUTTON_SECONDARY);
            }

            return event;
        }

        /**
         * Asserts that the specified view or one of its descendents has focus.
         */
+5 −49
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -328,60 +327,17 @@ public class DirectoryListBot extends Bots.BaseBot {

    public void rightClickDocument(Point point) throws UiObjectNotFoundException {
        // TODO: Use Espresso instead of doing the events mock ourselves
        MotionEvent motionDown = getTestMotionEvent(
                MotionEvent.ACTION_DOWN,
                MotionEvent.BUTTON_SECONDARY,
                MotionEvent.TOOL_TYPE_MOUSE,
                InputDevice.SOURCE_MOUSE,
                point.x,
                point.y);
        MotionEvent motionDown =
                getTestRightClickMotionEvent(MotionEvent.ACTION_DOWN, point.x, point.y);
        mAutomation.injectInputEvent(motionDown, true);
        SystemClock.sleep(100);

        MotionEvent motionUp = getTestMotionEvent(
                MotionEvent.ACTION_UP,
                MotionEvent.BUTTON_SECONDARY,
                MotionEvent.TOOL_TYPE_MOUSE,
                InputDevice.SOURCE_MOUSE,
                point.x,
                point.y);
        MotionEvent motionUp =
                getTestRightClickMotionEvent(MotionEvent.ACTION_UP, point.x, point.y);

        mAutomation.injectInputEvent(motionUp, true);
    }

    private MotionEvent getTestMotionEvent(
            int action, int buttonState, int toolType, int source, int x, int y) {
        long eventTime = SystemClock.uptimeMillis();

        MotionEvent.PointerProperties[] pp = {new MotionEvent.PointerProperties()};
        pp[0].clear();
        pp[0].id = 0;
        pp[0].toolType = toolType;

        MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
        pointerCoords[0].clear();
        pointerCoords[0].x = x;
        pointerCoords[0].y = y;
        pointerCoords[0].pressure = 0;
        pointerCoords[0].size = 1;

        return MotionEvent.obtain(
                eventTime,
                eventTime,
                action,
                1,
                pp,
                pointerCoords,
                0,
                buttonState,
                1f,
                1f,
                0,
                0,
                source,
                0);
    }

    private void assertOrder(String first, String second) throws UiObjectNotFoundException {

        final UiObject firstObj = findDocument(first);
+38 −1
Original line number Diff line number Diff line
@@ -21,8 +21,12 @@ import static androidx.test.espresso.action.ViewActions.swipeLeft;
import static androidx.test.espresso.action.ViewActions.swipeRight;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

import android.app.UiAutomation;
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.test.uiautomator.UiDevice;
@@ -47,9 +51,11 @@ public class SidebarBot extends Bots.BaseBot {
    private static final String TAG = "RootsListBot";

    private final String mRootListId;
    private final UiAutomation mAutomation;

    public SidebarBot(UiDevice device, Context context, int timeout) {
    public SidebarBot(UiDevice device, UiAutomation automation, Context context, int timeout) {
        super(device, context, timeout);
        mAutomation = automation;
        mRootListId = mTargetPackage + ":id/roots_list";
    }

@@ -135,4 +141,35 @@ public class SidebarBot extends Bots.BaseBot {
    public void assertHasFocus() {
        assertHasFocus(mRootListId);
    }

    /** Right clicks a root with `label`. */
    public void rightClickRoot(String label) throws UiObjectNotFoundException {
        Rect point = findRoot(label).getVisibleBounds();

        // The RootsFragment listens to right clicks in the GenericMotionListener. This is to allow
        // for a left and right click to be used interchangeably. This means to mock this behaviour,
        // 4 input events needs to be synthesized. A down, button press, button release and an up.
        MotionEvent motionDown =
                getTestRightClickMotionEvent(
                        MotionEvent.ACTION_DOWN, point.centerX(), point.centerY());
        mAutomation.injectInputEvent(motionDown, true);
        SystemClock.sleep(25);

        MotionEvent motionButtonPress =
                getTestRightClickMotionEvent(
                        MotionEvent.ACTION_BUTTON_PRESS, point.centerX(), point.centerY());
        mAutomation.injectInputEvent(motionButtonPress, true);
        SystemClock.sleep(25);

        MotionEvent motionButtonRelease =
                getTestRightClickMotionEvent(
                        MotionEvent.ACTION_BUTTON_RELEASE, point.centerX(), point.centerY());
        mAutomation.injectInputEvent(motionButtonRelease, true);
        SystemClock.sleep(25);

        MotionEvent motionUp =
                getTestRightClickMotionEvent(
                        MotionEvent.ACTION_UP, point.centerX(), point.centerY());
        mAutomation.injectInputEvent(motionUp, true);
    }
}
Loading