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

Commit 6b0265ee authored by Kyle Horimoto's avatar Kyle Horimoto
Browse files

Add BandSelectManager.

BUG:20669231

Change-Id: Iea8c3866a9de020a7bebd93a967aa42d45f8e2bd
parent 75952bac
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2015 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
  -->

<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    <solid android:color="@color/band_select_background" />
    <stroke android:width="1dp" android:color="@color/band_select_border" />
</shape>
+2 −0
Original line number Diff line number Diff line
@@ -27,4 +27,6 @@
    <color name="item_doc_grid_protect_background">#88000000</color>
    <color name="status_bar_background">@color/material_blue_700</color>
    <color name="action_mode_status_bar_background">@color/material_grey_800</color>
    <color name="band_select_background">#88ffffff</color>
    <color name="band_select_border">#44000000</color>
</resources>
+154 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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;

import static com.android.internal.util.Preconditions.checkState;

import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * Provides mouse driven band-select support when used in conjuction with {@link RecyclerView} and
 * {@link MultiSelectManager}. This class is responsible for rendering the band select overlay and
 * selecting overlaid items via MultiSelectManager.
 */
public class BandSelectManager extends RecyclerView.SimpleOnItemTouchListener {

    // For debugging purposes.
    private static final String TAG = "BandSelectManager";
    private static final boolean DEBUG = false;

    private final RecyclerView mRecyclerView;
    private final MultiSelectManager mSelectManager;
    private final Drawable mRegionSelectorDrawable;

    private boolean mIsBandSelectActive = false;
    private Point mOrigin;
    private Rect mRegionBounds;

    /**
     * @param recyclerView
     * @param multiSelectManager
     */
    public BandSelectManager(RecyclerView recyclerView, MultiSelectManager multiSelectManager) {
        mRecyclerView = recyclerView;
        mSelectManager = multiSelectManager;
        mRegionSelectorDrawable =
            mRecyclerView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);

        mRecyclerView.addOnItemTouchListener(this);
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        // Only intercept the event if it was triggered by a mouse. If band select is inactive,
        // do not intercept ACTION_UP events as they will not be processed.
        return isMouseEvent(e) &&
                (mIsBandSelectActive || e.getActionMasked() != MotionEvent.ACTION_UP);
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        checkState(isMouseEvent(e));
        processMotionEvent(e);
    }

    /**
     * Processes a MotionEvent by starting, ending, or resizing the band select overlay.
     * @param e
     */
    private void processMotionEvent(MotionEvent e) {
        if (mIsBandSelectActive && e.getActionMasked() == MotionEvent.ACTION_UP) {
            endBandSelect();
            return;
        }

        Point point = new Point((int) e.getX(), (int) e.getY());
        if (!mIsBandSelectActive) {
            startBandSelect(point);
        }

        resizeBandSelectRectangle(point);
        selectChildrenCoveredBySelection();
    }

    /**
     * Starts band select by adding the drawable to the RecyclerView's overlay.
     * @param origin The starting point of the selection.
     */
    private void startBandSelect(Point origin) {
        if (DEBUG) Log.d(TAG, "Starting band select from (" + origin.x + "," + origin.y + ").");
        mIsBandSelectActive = true;
        mOrigin = origin;
        mRecyclerView.getOverlay().add(mRegionSelectorDrawable);
    }

    /**
     * Resizes the band select rectangle by using the origin and the current pointer positoin as
     * two opposite corners of the selection.
     * @param pointerPosition
     */
    private void resizeBandSelectRectangle(Point pointerPosition) {
        mRegionBounds = new Rect(Math.min(mOrigin.x, pointerPosition.x),
                Math.min(mOrigin.y, pointerPosition.y),
                Math.max(mOrigin.x, pointerPosition.x),
                Math.max(mOrigin.y, pointerPosition.y));
        mRegionSelectorDrawable.setBounds(mRegionBounds);
    }

    /**
     * Selects the children covered by the band select overlay by delegating to MultiSelectManager.
     * TODO: Provide a finished implementation. This is down and dirty, proof of concept code.
     * Final optimized implementation, with support for managing offscreen selection to come.
     */
    private void selectChildrenCoveredBySelection() {
        for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
            View child = mRecyclerView.getChildAt(i);
            ViewHolder holder = mRecyclerView.getChildViewHolder(child);
            Rect childRect = new Rect();
            child.getHitRect(childRect);

            boolean doRectsOverlap = Rect.intersects(childRect, mRegionBounds);
            mSelectManager.setItemSelected(holder.getAdapterPosition(), doRectsOverlap);
        }
    }

    /**
     * Ends band select by removing the overlay.
     */
    private void endBandSelect() {
        if (DEBUG) Log.d(TAG, "Ending band select.");
        mIsBandSelectActive = false;
        mRecyclerView.getOverlay().remove(mRegionSelectorDrawable);
    }

    /**
     * Determines whether the provided event was triggered by a mouse (as opposed to a finger or
     * stylus).
     * @param e
     * @return
     */
    private static boolean isMouseEvent(MotionEvent e) {
        return e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -397,6 +397,7 @@ public class DirectoryFragment extends Fragment {
        // Kick off loader at least once
        getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);

        mFragmentTuner.afterActivityCreated(this);
        updateDisplayState();
    }

@@ -1662,6 +1663,7 @@ public class DirectoryFragment extends Fragment {
     */
    private interface FragmentTuner {
        void updateActionMenu(Menu menu, int dirType, boolean canDelete);
        void afterActivityCreated(DirectoryFragment fragment);
    }

    /**
@@ -1719,6 +1721,9 @@ public class DirectoryFragment extends Fragment {
            // Only shown in standalone mode.
            copyToClipboard.setVisible(false);
        }

        @Override
        public void afterActivityCreated(DirectoryFragment fragment) {}
    }

    /**
@@ -1735,5 +1740,10 @@ public class DirectoryFragment extends Fragment {
            menu.findItem(R.id.menu_copy_to).setVisible(false);
            menu.findItem(R.id.menu_move_to).setVisible(false);
        }

        @Override
        public void afterActivityCreated(DirectoryFragment fragment) {
            new BandSelectManager(fragment.mRecView, fragment.mSelectionManager);
        }
    }
}