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

Commit fb6744d6 authored by Steve McKay's avatar Steve McKay Committed by Android (Google) Code Review
Browse files

Merge "Add BandSelectManager."

parents 1ad82779 6b0265ee
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);
        }
    }
}