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

Commit 5dce2b88 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add gesture scaling support for grid mode." into nyc-andromeda-dev

parents a3965885 b03a59c4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
    <dimen name="list_item_thumbnail_size">40dp</dimen>
    <dimen name="grid_item_icon_size">30dp</dimen>
    <dimen name="progress_bar_height">4dp</dimen>
    <fraction name="grid_scale_min">85%</fraction>
    <fraction name="grid_scale_max">200%</fraction>
    <dimen name="grid_width">152dp</dimen>
    <dimen name="grid_height">176dp</dimen>
    <dimen name="grid_item_width">152dp</dimen>
+1 −1
Original line number Diff line number Diff line
@@ -731,7 +731,7 @@ public abstract class BaseActivity<T extends ActionHandler>
        });
    }

    public final class RetainedState {
    public static final class RetainedState {
        public @Nullable Selection selection;

        public boolean hasSelection() {
+45 −0
Original line number Diff line number Diff line
@@ -13,36 +13,33 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.documentsui.queries;

import android.text.TextUtils;
import android.util.Log;

import com.android.documentsui.base.EventHandler;

public class SetQuickViewerCommand implements EventHandler<String[]> {

    // This is a quick/easy shortcut to sharing quick viewer debug settings
    // with QuickViewIntent builder. Tried setting at a system property
    // but got a native error. This being quick and easy, didn't investigate that err.
    public static String sQuickViewer;
    private static final String TAG = "SetQuickViewerCommand";

    @Override
    public boolean accept(String[] tokens) {
        if ("setqv".equals(tokens[0])) {
            if (tokens.length == 2 && !TextUtils.isEmpty(tokens[1])) {
                sQuickViewer = tokens[1];
                Log.i(TAG, "Set quick viewer to: " + sQuickViewer);
                return true;
            } else {
                Log.w(TAG, "Invalid command structure: " + tokens);
package com.android.documentsui.base;

import javax.annotation.Nullable;

/**
 * Shared values that may be set by {@link DebugCommandProcessor}.
 */
public final class DebugFlags {

    private DebugFlags() {}

    private static String mQvPackage;
    private static boolean sGestureScaleEnabled;

    public static void setQuickViewer(@Nullable String qvPackage) {
        mQvPackage = qvPackage;
    }
        } else if ("unsetqv".equals(tokens[0])) {
            Log.i(TAG, "Unset quick viewer");
            sQuickViewer = null;
            return true;

    public static @Nullable String getQuickViewer() {
        return mQvPackage;
    }
        return false;

    public static void setGestureScaleEnabled(boolean enabled) {
        sGestureScaleEnabled = enabled;
    }

    public static boolean getGestureScaleEnabled() {
        return sGestureScaleEnabled;
    }
}
+61 −9
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import static com.android.documentsui.base.Shared.DEBUG;
import static com.android.documentsui.base.State.MODE_GRID;
import static com.android.documentsui.base.State.MODE_LIST;

import android.annotation.DimenRes;
import android.annotation.FractionRes;
import android.annotation.IntDef;
import android.annotation.StringRes;
import android.app.Activity;
@@ -36,6 +38,7 @@ import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -66,8 +69,8 @@ import com.android.documentsui.ActivityConfig;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.BaseActivity.RetainedState;
import com.android.documentsui.DirectoryLoader;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.DirectoryReloadLock;
import com.android.documentsui.DirectoryResult;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.FocusManager;
import com.android.documentsui.ItemDragListener;
@@ -183,6 +186,9 @@ public class DirectoryFragment extends Fragment
    private GridLayoutManager mLayout;
    private int mColumnCount = 1;  // This will get updated when layout changes.

    private float mLiveScale = 1.0f;
    private int mMode;

    private MessageBar mMessageBar;
    private View mProgressBar;

@@ -204,7 +210,7 @@ public class DirectoryFragment extends Fragment
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        BaseActivity activity = (BaseActivity<?>) getActivity();
        BaseActivity<?> activity = (BaseActivity<?>) getActivity();
        final View view = inflater.inflate(R.layout.fragment_directory, container, false);

        mMessageBar = MessageBar.create(getChildFragmentManager());
@@ -342,6 +348,7 @@ public class DirectoryFragment extends Fragment
        EventHandler<InputEvent> gestureHandler = mState.allowMultiple
                ? gestureSel::start
                : EventHandler.createStub(false);

        mInputHandler = new UserInputHandler<>(
                mActions,
                mFocusManager,
@@ -359,7 +366,8 @@ public class DirectoryFragment extends Fragment
                mDragStartListener::onMouseDragEvent,
                gestureSel,
                mInputHandler,
                mBandController);
                mBandController,
                this::scaleLayout);

        mMenuManager = mActivity.getMenuManager();

@@ -497,6 +505,7 @@ public class DirectoryFragment extends Fragment
     * @param mode The new view mode.
     */
    private void updateLayout(@ViewMode int mode) {
        mMode = mode;
        mColumnCount = calculateColumnCount(mode);
        if (mLayout != null) {
            mLayout.setSpanCount(mColumnCount);
@@ -511,22 +520,65 @@ public class DirectoryFragment extends Fragment
        mIconHelper.setViewMode(mode);
    }

    /**
     * Updates the layout after the view mode switches.
     * @param mode The new view mode.
     */
    private void scaleLayout(float scale) {
        assert(Build.IS_DEBUGGABLE);
        if (DEBUG) Log.v(TAG, "Handling scale event: " + scale + ", existing scale: " + mLiveScale);

        if (mMode == MODE_GRID) {
            float minScale = getFraction(R.fraction.grid_scale_min);
            float maxScale = getFraction(R.fraction.grid_scale_max);
            float nextScale = mLiveScale * scale;

            if (DEBUG) Log.v(TAG,
                    "Next scale " + nextScale + ", Min/max scale " + minScale + "/" + maxScale);

            if (nextScale > minScale && nextScale < maxScale) {
                if (DEBUG) Log.d(TAG, "Updating grid scale: " + scale);
                mLiveScale = nextScale;
                updateLayout(mMode);
            }

        } else {
            if (DEBUG) Log.d(TAG, "List mode, ignoring scale: " + scale);
            mLiveScale = 1.0f;
        }
    }

    private int calculateColumnCount(@ViewMode int mode) {
        if (mode == MODE_LIST) {
            // List mode is a "grid" with 1 column.
            return 1;
        }

        int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width);
        int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
        int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight();
        int cellWidth = getScaledSize(R.dimen.grid_width);
        int cellMargin = 2 * getScaledSize(R.dimen.grid_item_margin);
        int viewPadding =
                (int) ((mRecView.getPaddingLeft() + mRecView.getPaddingRight()) * mLiveScale);

        // RecyclerView sometimes gets a width of 0 (see b/27150284).  Clamp so that we always lay
        // out the grid with at least 2 columns.
        // RecyclerView sometimes gets a width of 0 (see b/27150284).
        // Clamp so that we always lay out the grid with at least 2 columns by default.
        int columnCount = Math.max(2,
                (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin));

        return columnCount;
        // Finally with our grid count logic firmly in place, we apply any live scaling
        // captured by the scale gesture detector.
        return Math.max(1, Math.round(columnCount / mLiveScale));
    }


    /**
     * Moderately abuse the "fraction" resource type for our purposes.
     */
    private float getFraction(@FractionRes int id) {
        return getResources().getFraction(id, 1, 0);
    }

    private int getScaledSize(@DimenRes int id) {
        return (int) (getResources().getDimensionPixelSize(id) * mLiveScale);
    }

    private int getDirectoryPadding(@ViewMode int mode) {
+40 −1
Original line number Diff line number Diff line
@@ -16,15 +16,21 @@

package com.android.documentsui.dirlist;

import static com.android.documentsui.base.Shared.DEBUG;

import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnTouchListener;

import com.android.documentsui.base.DebugFlags;
import com.android.documentsui.base.EventHandler;
import com.android.documentsui.base.Events;
import com.android.documentsui.base.Events.InputEvent;
@@ -32,18 +38,25 @@ import com.android.documentsui.base.Events.MotionInputEvent;
import com.android.documentsui.selection.BandController;
import com.android.documentsui.selection.GestureSelector;

import java.util.function.Consumer;

//Receives event meant for both directory and empty view, and either pass them to
//{@link UserInputHandler} for simple gestures (Single Tap, Long-Press), or intercept them for
//other types of gestures (drag n' drop)
final class ListeningGestureDetector extends GestureDetector
        implements OnItemTouchListener, OnTouchListener {

    private static final String TAG = "ListeningGestureDetector";

    private final GestureSelector mGestureSelector;
    private final EventHandler<InputEvent> mMouseDragListener;
    private final BandController mBandController;
    private final MouseDelegate mMouseDelegate = new MouseDelegate();
    private final TouchDelegate mTouchDelegate = new TouchDelegate();

    // Currently only initialized on IS_DEBUGGABLE builds.
    private final @Nullable ScaleGestureDetector mScaleDetector;

    public ListeningGestureDetector(
            Context context,
            RecyclerView recView,
@@ -51,19 +64,45 @@ final class ListeningGestureDetector extends GestureDetector
            EventHandler<InputEvent> mouseDragListener,
            GestureSelector gestureSelector,
            UserInputHandler<? extends InputEvent> handler,
            @Nullable BandController bandController) {
            @Nullable BandController bandController,
            Consumer<Float> scaleHandler) {

        super(context, handler);

        mMouseDragListener = mouseDragListener;
        mGestureSelector = gestureSelector;
        mBandController = bandController;
        recView.addOnItemTouchListener(this);
        emptyView.setOnTouchListener(this);

        mScaleDetector = !Build.IS_DEBUGGABLE
                ? null
                : new ScaleGestureDetector(
                        context,
                        new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                            @Override
                            public boolean onScale(ScaleGestureDetector detector) {
                                if (DEBUG) Log.v(TAG,
                                        "Received scale event: " + detector.getScaleFactor());
                                scaleHandler.accept(detector.getScaleFactor());
                                return true;
                            }
                        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        boolean handled = false;

        // This is an in-development feature.
        // TODO: Re-wire event handling so that we're not dispatching
        //     events to to scaledetector's #onTouchEvent from this
        //     #onInterceptTouchEvent touch event.
        if (DebugFlags.getGestureScaleEnabled()
                && mScaleDetector != null) {
            mScaleDetector.onTouchEvent(e);
        }

        try (InputEvent event = MotionInputEvent.obtain(e, rv)) {
            if (event.isMouseEvent()) {
                handled |= mMouseDelegate.onInterceptTouchEvent(event);
Loading