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

Commit a5588b65 authored by Garfield, Tan's avatar Garfield, Tan
Browse files

Allow root item accepts drag and drop.

Bug: 28315281
Change-Id: Ie37536258f04c1b0d3ff5a315f4a5fa33e0ba182
parent 9666ce69
Loading
Loading
Loading
Loading
+30 −13
Original line number Original line Diff line number Diff line
@@ -60,6 +60,8 @@ import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperations;


import java.io.FileNotFoundException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -71,6 +73,32 @@ import java.util.concurrent.Executor;
public abstract class BaseActivity extends Activity
public abstract class BaseActivity extends Activity
        implements SearchManagerListener, NavigationViewManager.Environment {
        implements SearchManagerListener, NavigationViewManager.Environment {


    public final FileOperations.Callback fileOpCallback = (status, opType, docCount) -> {
        if (status == FileOperations.Callback.STATUS_REJECTED) {
            Snackbars.showPasteFailed(this);
            return;
        }

        if (docCount == 0) {
            // Nothing has been pasted, so there is no need to show a snackbar.
            return;
        }

        switch (opType) {
            case FileOperationService.OPERATION_MOVE:
                Snackbars.showMove(this, docCount);
                break;
            case FileOperationService.OPERATION_COPY:
                Snackbars.showCopy(this, docCount);
                break;
            case FileOperationService.OPERATION_DELETE:
                // We don't show anything for deletion.
                break;
            default:
                throw new UnsupportedOperationException("Unsupported Operation: " + opType);
        }
    };

    private static final String BENCHMARK_TESTING_PACKAGE = "com.android.documentsui.appperftests";
    private static final String BENCHMARK_TESTING_PACKAGE = "com.android.documentsui.appperftests";


    State mState;
    State mState;
@@ -707,17 +735,6 @@ public abstract class BaseActivity extends Activity
        mNavDrawerHasFocus = !mNavDrawerHasFocus;
        mNavDrawerHasFocus = !mNavDrawerHasFocus;
    }
    }


    DocumentInfo getRootDocumentBlocking(RootInfo root) {
        try {
            final Uri uri = DocumentsContract.buildDocumentUri(
                    root.authority, root.documentId);
            return DocumentInfo.fromUri(getContentResolver(), uri);
        } catch (FileNotFoundException e) {
            Log.w(mTag, "Failed to find root", e);
            return null;
        }
    }

    /**
    /**
     * Pops the top entry off the directory stack, and returns the user to the previous directory.
     * Pops the top entry off the directory stack, and returns the user to the previous directory.
     * If the directory stack only contains one item, this method does nothing.
     * If the directory stack only contains one item, this method does nothing.
@@ -781,7 +798,7 @@ public abstract class BaseActivity extends Activity


        @Override
        @Override
        protected DocumentInfo run(Void... params) {
        protected DocumentInfo run(Void... params) {
            return mOwner.getRootDocumentBlocking(mRoot);
            return mRoot.getRootDocumentBlocking(mOwner);
        }
        }


        @Override
        @Override
@@ -817,7 +834,7 @@ public abstract class BaseActivity extends Activity
            final RootInfo defaultRoot = mOwner.mRoots.getDefaultRootBlocking(mOwner.mState);
            final RootInfo defaultRoot = mOwner.mRoots.getDefaultRootBlocking(mOwner.mState);
            assert(defaultRoot != null);
            assert(defaultRoot != null);
            if (!defaultRoot.isRecents()) {
            if (!defaultRoot.isRecents()) {
                mDefaultRootDocument = mOwner.getRootDocumentBlocking(defaultRoot);
                mDefaultRootDocument = defaultRoot.getRootDocumentBlocking(mOwner);
            }
            }
            return defaultRoot;
            return defaultRoot;
        }
        }
+1 −0
Original line number Original line Diff line number Diff line
@@ -73,6 +73,7 @@ abstract class CheckedTask<Input, Output>
        finish(result);
        finish(result);
    }
    }


    @FunctionalInterface
    interface Check {
    interface Check {
        boolean stop();
        boolean stop();
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -520,7 +520,7 @@ public class FilesActivity extends BaseActivity {
            } catch (FileNotFoundException e) {
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
                Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
            }
            }
            mState.stack.add(mOwner.getRootDocumentBlocking(root));
            mState.stack.add(root.getRootDocumentBlocking(mOwner));
            return null;
            return null;
        }
        }


+2 −3
Original line number Original line Diff line number Diff line
@@ -22,7 +22,6 @@ import android.util.Log;
import android.view.DragEvent;
import android.view.DragEvent;
import android.view.View;
import android.view.View;
import android.view.View.OnDragListener;
import android.view.View.OnDragListener;
import android.view.ViewConfiguration;


import com.android.documentsui.ItemDragListener.DragHost;
import com.android.documentsui.ItemDragListener.DragHost;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -39,7 +38,7 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener {
    private static final String TAG = "ItemDragListener";
    private static final String TAG = "ItemDragListener";


    @VisibleForTesting
    @VisibleForTesting
    static final int SPRING_TIMEOUT = ViewConfiguration.getLongPressTimeout();
    static final int SPRING_TIMEOUT = 1000;


    protected final H mDragHost;
    protected final H mDragHost;
    private final Timer mHoverTimer;
    private final Timer mHoverTimer;
@@ -82,7 +81,7 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener {
        TimerTask task = createOpenTask(v);
        TimerTask task = createOpenTask(v);
        assert (task != null);
        assert (task != null);
        v.setTag(R.id.drag_hovering_tag, task);
        v.setTag(R.id.drag_hovering_tag, task);
        mHoverTimer.schedule(task, ViewConfiguration.getLongPressTimeout());
        mHoverTimer.schedule(task, SPRING_TIMEOUT);
    }
    }


    private void handleLocationEvent(View v, float x, float y) {
    private void handleLocationEvent(View v, float x, float y) {
+70 −8
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ClipData;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.Loader;
import android.content.Loader;
@@ -33,16 +34,14 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Looper;
import android.os.Looper;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.provider.Settings;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Formatter;
import android.util.Log;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.MotionEvent;
@@ -60,7 +59,11 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView;


import com.android.documentsui.CheckedTask.Check;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.services.FileOperations;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
@@ -81,6 +84,20 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost
    private static final String TAG = "RootsFragment";
    private static final String TAG = "RootsFragment";
    private static final String EXTRA_INCLUDE_APPS = "includeApps";
    private static final String EXTRA_INCLUDE_APPS = "includeApps";


    private final OnDragListener mDragListener = new ItemDragListener<RootsFragment>(this) {
        @Override
        public boolean handleDropEventChecked(View v, DragEvent event) {
            final int position = (Integer) v.getTag(R.id.item_position_tag);
            final Item item = mAdapter.getItem(position);

            assert(item.isDropTarget());

            BaseActivity activity = getBaseActivity();
            return item.dropOn(event.getClipData(), activity, RootsFragment.this::isDetached,
                    activity.fileOpCallback);
        }
    };

    private ListView mList;
    private ListView mList;
    private RootsAdapter mAdapter;
    private RootsAdapter mAdapter;
    private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
    private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
@@ -151,8 +168,8 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost


                Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
                Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);


                mAdapter = new RootsAdapter(context, result, handlerAppIntent, state,
                mAdapter =
                        new ItemDragListener<>(RootsFragment.this));
                        new RootsAdapter(context, result, handlerAppIntent, state, mDragListener);
                mList.setAdapter(mAdapter);
                mList.setAdapter(mAdapter);


                onCurrentRootChanged();
                onCurrentRootChanged();
@@ -233,12 +250,12 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost
     * In RootsFragment we open the hovered root.
     * In RootsFragment we open the hovered root.
     */
     */
    @Override
    @Override
    public void onViewHovered(View view) {
    public void onViewHovered(View v) {
        // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
        // SpacerView doesn't have DragListener so this view is guaranteed to be a RootItemView.
        RootItemView itemView = (RootItemView) view;
        RootItemView itemView = (RootItemView) v;
        itemView.drawRipple();
        itemView.drawRipple();


        final int position = (Integer) view.getTag(R.id.item_position_tag);
        final int position = (Integer) v.getTag(R.id.item_position_tag);
        final Item item = mAdapter.getItem(position);
        final Item item = mAdapter.getItem(position);
        item.open(this);
        item.open(this);
    }
    }
@@ -360,6 +377,11 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost
        abstract boolean isDropTarget();
        abstract boolean isDropTarget();


        abstract void open(RootsFragment fragment);
        abstract void open(RootsFragment fragment);

        boolean dropOn(ClipData data, Context context, Check check,
                FileOperations.Callback callback) {
            return false;
        }
    }
    }


    private static class RootItem extends Item {
    private static class RootItem extends Item {
@@ -426,6 +448,14 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost
            Metrics.logRootVisited(fragment.getActivity(), root);
            Metrics.logRootVisited(fragment.getActivity(), root);
            activity.onRootPicked(root);
            activity.onRootPicked(root);
        }
        }

        @Override
        boolean dropOn(
                ClipData data, Context context, Check check, FileOperations.Callback callback) {
            ProviderExecutor executor = ProviderExecutor.forAuthority(root.authority);
            new DropOnRootTask(data, root, context, check, callback).executeOnExecutor(executor);
            return true;
        }
    }
    }


    private static class SpacerItem extends Item {
    private static class SpacerItem extends Item {
@@ -504,6 +534,38 @@ public class RootsFragment extends Fragment implements ItemDragListener.DragHost
        }
        }
    }
    }


    private static class DropOnRootTask extends CheckedTask<Void, DocumentInfo> {
        private ClipData mData;
        private RootInfo mDstRoot;
        private Context mContext;
        private FileOperations.Callback mCallback;

        private DropOnRootTask(ClipData data, RootInfo dstRoot, Context context, Check check,
                FileOperations.Callback callback) {
            super(check);
            mData = data;
            mDstRoot = dstRoot;
            mContext = context;
            mCallback = callback;
        }

        @Override
        public DocumentInfo run(Void... args) {
            return mDstRoot.getRootDocumentBlocking(mContext);
        }

        @Override
        public void finish(DocumentInfo doc) {
            if (doc != null) {
                DocumentClipper clipper =
                        DocumentsApplication.getDocumentClipper(mContext);
                clipper.copyFromClipData(mDstRoot, doc, mData, mCallback);
            } else {
                Log.e(TAG, "Failed to get doc.");
            }
        }
    }

    private static class RootsAdapter extends ArrayAdapter<Item> {
    private static class RootsAdapter extends ArrayAdapter<Item> {
        private static final Map<String, Long> sIdMap = new HashMap<String, Long>();
        private static final Map<String, Long> sIdMap = new HashMap<String, Long>();
        // the next available id to associate with a new string id
        // the next available id to associate with a new string id
Loading