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

Commit 2499e8de authored by Steve McKay's avatar Steve McKay
Browse files

Move GestureListener to own file.

And:
- make MotionInputEvent AutoCloseable (and update obtainers accordingly).
- remove unused constructor args and overloading from MultiSelectManager.

Change-Id: I335a95c3d05ab10bdcbfebab8dc69f0b2f681e3a
parent fd9f5e60
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ public final class Events {
        int getItemPosition();
    }

    public static final class MotionInputEvent implements InputEvent {
    public static final class MotionInputEvent implements InputEvent, AutoCloseable {
        private static final String TAG = "MotionInputEvent";

        private static final Pools.SimplePool<MotionInputEvent> sPool = new Pools.SimplePool<>(1);
@@ -199,6 +199,11 @@ public final class Events {
            assert(released);
        }

        @Override
        public void close() {
            recycle();
        }

        @Override
        public boolean isMouseEvent() {
            return Events.isMouseEvent(mEvent);
+4 −8
Original line number Diff line number Diff line
@@ -52,13 +52,13 @@ import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DurableUtils;
import com.android.documentsui.model.RootInfo;

import libcore.io.IoUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import libcore.io.IoUtils;

/**
 * Display directories where recent creates took place.
 */
@@ -141,17 +141,13 @@ public class RecentsCreateFragment extends Fragment {
            new RecyclerView.OnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                    final MotionInputEvent event = MotionInputEvent.obtain(e, mRecView);
                    try {
                    try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
                        if (event.isOverItem() && event.isActionUp()) {
                            final DocumentStack stack = mAdapter.getItem(event.getItemPosition());
                            ((BaseActivity) getActivity()).onStackPicked(stack);
                            return true;
                        }

                        return false;
                    } finally {
                        event.recycle();
                    }
                }

@@ -247,7 +243,7 @@ public class RecentsCreateFragment extends Fragment {

          final LayoutInflater inflater = LayoutInflater.from(context);
          return new StackHolder(
                  (View) inflater.inflate(R.layout.item_doc_list, parent, false));
                  inflater.inflate(R.layout.item_doc_list, parent, false));
        }

        @Override
+2 −8
Original line number Diff line number Diff line
@@ -85,21 +85,15 @@ public class BandController extends RecyclerView.OnScrollListener {
                new RecyclerView.OnItemTouchListener() {
                    @Override
                    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                        final MotionInputEvent event = MotionInputEvent.obtain(e, view);
                        try {
                        try (MotionInputEvent event = MotionInputEvent.obtain(e, view)) {
                            return handleEvent(event);
                        } finally {
                            event.recycle();
                        }
                    }
                    @Override
                    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
                        if (Events.isMouseEvent(e)) {
                            final MotionInputEvent event = MotionInputEvent.obtain(e, view);
                            try {
                            try (MotionInputEvent event = MotionInputEvent.obtain(e, view)) {
                                processInputEvent(event);
                            } finally {
                                event.recycle();
                            }
                        }
                    }
+46 −125
Original line number Diff line number Diff line
@@ -58,13 +58,11 @@ import android.util.SparseArray;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -74,11 +72,9 @@ import android.widget.Toolbar;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DirectoryLoader;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.DocumentClipper;
import com.android.documentsui.DocumentsActivity;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.Events;
import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.ItemDragListener;
import com.android.documentsui.MenuManager;
@@ -94,6 +90,7 @@ import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
import com.android.documentsui.State;
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.UrisSupplier;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -295,22 +292,27 @@ public class DirectoryFragment extends Fragment
        }
        mRecView.setLayoutManager(mLayout);

        mGestureDetector =
                new ListeningGestureDetector(this.getContext(), mDragHelper, new GestureListener());

        mRecView.addOnItemTouchListener(mGestureDetector);
        mEmptyView.setOnTouchListener(mGestureDetector);

        // TODO: instead of inserting the view into the constructor, extract listener-creation code
        // and set the listener on the view after the fact.  Then the view doesn't need to be passed
        // into the selection manager.
        mSelectionManager = new MultiSelectManager(
                mRecView,
                mAdapter,
                state.allowMultiple
                    ? MultiSelectManager.MODE_MULTIPLE
                    : MultiSelectManager.MODE_SINGLE,
                null);
                    : MultiSelectManager.MODE_SINGLE);

        GestureListener gestureListener = new GestureListener(
                mSelectionManager,
                mRecView,
                this::getTarget,
                this::onDoubleTap,
                this::onRightClick);

        mGestureDetector =
                new ListeningGestureDetector(this.getContext(), mDragHelper, gestureListener);

        mRecView.addOnItemTouchListener(mGestureDetector);
        mEmptyView.setOnTouchListener(mGestureDetector);

        if (state.allowMultiple) {
            mBandController = new BandController(mRecView, mAdapter, mSelectionManager);
@@ -426,12 +428,9 @@ public class DirectoryFragment extends Fragment
        return false;
    }

    protected boolean onRightClick(MotionEvent e) {
        // First get target to see if it's a blank window or a file/doc
        final MotionInputEvent event = MotionInputEvent.obtain(e, mRecView);
        try {
            if (event.getItemPosition() != RecyclerView.NO_POSITION) {
                final DocumentHolder holder = getTarget(event);
    protected boolean onRightClick(MotionInputEvent e) {
        if (e.getItemPosition() != RecyclerView.NO_POSITION) {
            final DocumentHolder holder = getTarget(e);
            String modelId = getModelId(holder.itemView);
            if (!mSelectionManager.getSelection().contains(modelId)) {
                mSelectionManager.clearSelection();
@@ -462,9 +461,6 @@ public class DirectoryFragment extends Fragment
            mRecView.showContextMenu(e.getX(), e.getY());
            unregisterForContextMenu(mRecView);
        }
        } finally {
            event.recycle();
        }
        return true;
    }

@@ -1554,81 +1550,6 @@ public class DirectoryFragment extends Fragment
        return mTuner.canSelectType(docMimeType, docFlags);
    }

    /**
     * The gesture listener for items in the list/grid view. Interprets gestures and sends the
     * events to the target DocumentHolder, whence they are routed to the appropriate listener.
     */
    class GestureListener extends GestureDetector.SimpleOnGestureListener {
        // From the RecyclerView, we get two events sent to
        // ListeningGestureDetector#onInterceptTouchEvent on a mouse click; we first get an
        // ACTION_DOWN Event for clicking on the mouse, and then an ACTION_UP event from releasing
        // the mouse click. ACTION_UP event doesn't have information regarding the button (primary
        // vs. secondary), so we have to save that somewhere first from ACTION_DOWN, and then reuse
        // it later. The ACTION_DOWN event doesn't get forwarded to GestureListener, so we have open
        // up a public set method to set it.
        private int mLastButtonState = -1;

        public void setLastButtonState(int state) {
            mLastButtonState = state;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // Single tap logic:
            // We first see if it's a mouse event, and if it was right click by checking on
            // @{code ListeningGestureDetector#mLastButtonState}
            // If the selection manager is active, it gets first whack at handling tap
            // events. Otherwise, tap events are routed to the target DocumentHolder.
            if (Events.isMouseEvent(e) && mLastButtonState == MotionEvent.BUTTON_SECONDARY) {
                mLastButtonState = -1;
                return onRightClick(e);
            }

            final MotionInputEvent event = MotionInputEvent.obtain(e, mRecView);
            try {
                boolean handled = mSelectionManager.onSingleTapUp(event);

                if (handled) {
                    return handled;
                }

                // Give the DocumentHolder a crack at the event.
                DocumentHolder holder = DirectoryFragment.this.getTarget(event);
                if (holder != null) {
                    handled = holder.onSingleTapUp(e);
                }

                return handled;
            } finally {
                event.recycle();
            }
        }

        @Override
        public void onLongPress(MotionEvent e) {
            // Long-press events get routed directly to the selection manager. They can be
            // changed to route through the DocumentHolder if necessary.
            final MotionInputEvent event = MotionInputEvent.obtain(e, mRecView);
            try {
                mSelectionManager.onLongPress(event);
            } finally {
                event.recycle();
            }
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            // Double-tap events are handled directly by the DirectoryFragment. They can be changed
            // to route through the DocumentHolder if necessary.
            final MotionInputEvent event = MotionInputEvent.obtain(e, mRecView);
            return DirectoryFragment.this.onDoubleTap(event);
        }

        public boolean onRightClick(MotionEvent e) {
            return DirectoryFragment.this.onRightClick(e);
        }
    }

    public static void showDirectory(
            FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
        create(fm, TYPE_NORMAL, root, doc, null, anim);
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.documentsui.dirlist;

import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;

import com.android.documentsui.Events;
import com.android.documentsui.Events.MotionInputEvent;

import java.util.function.Function;
import java.util.function.Predicate;

/**
 * The gesture listener for items in the directly list, interprets gestures, and sends the
 * events to the target DocumentHolder, whence they are routed to the appropriate listener.
 */
final class GestureListener extends GestureDetector.SimpleOnGestureListener {
    // From the RecyclerView, we get two events sent to
    // ListeningGestureDetector#onInterceptTouchEvent on a mouse click; we first get an
    // ACTION_DOWN Event for clicking on the mouse, and then an ACTION_UP event from releasing
    // the mouse click. ACTION_UP event doesn't have information regarding the button (primary
    // vs. secondary), so we have to save that somewhere first from ACTION_DOWN, and then reuse
    // it later. The ACTION_DOWN event doesn't get forwarded to GestureListener, so we have open
    // up a public set method to set it.
    private int mLastButtonState = -1;
    private MultiSelectManager mSelectionMgr;
    private RecyclerView mRecView;
    private Function<MotionInputEvent, DocumentHolder> mDocFinder;
    private Predicate<MotionInputEvent> mDoubleTapHandler;
    private Predicate<MotionInputEvent> mRightClickHandler;

    public GestureListener(
            MultiSelectManager selectionMgr,
            RecyclerView recView,
            Function<MotionInputEvent, DocumentHolder> docFinder,
            Predicate<MotionInputEvent> doubleTapHandler,
            Predicate<MotionInputEvent> rightClickHandler) {
        mSelectionMgr = selectionMgr;
        mRecView = recView;
        mDocFinder = docFinder;
        mDoubleTapHandler = doubleTapHandler;
        mRightClickHandler = rightClickHandler;
    }

    public void setLastButtonState(int state) {
        mLastButtonState = state;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        // Single tap logic:
        // We first see if it's a mouse event, and if it was right click by checking on
        // @{code ListeningGestureDetector#mLastButtonState}
        // If the selection manager is active, it gets first whack at handling tap
        // events. Otherwise, tap events are routed to the target DocumentHolder.
        if (Events.isMouseEvent(e) && mLastButtonState == MotionEvent.BUTTON_SECONDARY) {
            mLastButtonState = -1;
            return onRightClick(e);
        }

        try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
            boolean handled = mSelectionMgr.onSingleTapUp(event);

            if (handled) {
                return handled;
            }

            // Give the DocumentHolder a crack at the event.
            DocumentHolder holder = mDocFinder.apply(event);
            if (holder != null) {
                handled = holder.onSingleTapUp(e);
            }

            return handled;
        }
    }

    @Override
    public void onLongPress(MotionEvent e) {
        // Long-press events get routed directly to the selection manager. They can be
        // changed to route through the DocumentHolder if necessary.
        try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
            mSelectionMgr.onLongPress(event);
        }
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        // Double-tap events are handled directly by the DirectoryFragment. They can be changed
        // to route through the DocumentHolder if necessary.

        try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
            return mDoubleTapHandler.test(event);
        }
    }

    public boolean onRightClick(MotionEvent e) {
        try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
            return mRightClickHandler.test(event);
        }
    }
}
 No newline at end of file
Loading