1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui.dirlist;
18 
19 import static com.android.documentsui.base.SharedMinimal.DEBUG;
20 
21 import android.app.Activity;
22 import android.content.ClipData;
23 import android.util.Log;
24 import android.view.DragEvent;
25 import android.view.View;
26 
27 import androidx.recyclerview.selection.SelectionTracker;
28 
29 import com.android.documentsui.AbstractActionHandler;
30 import com.android.documentsui.AbstractDragHost;
31 import com.android.documentsui.ActionHandler;
32 import com.android.documentsui.DragAndDropManager;
33 import com.android.documentsui.Metrics;
34 import com.android.documentsui.R;
35 import com.android.documentsui.base.DocumentInfo;
36 import com.android.documentsui.base.DocumentStack;
37 import com.android.documentsui.base.Lookup;
38 import com.android.documentsui.base.State;
39 import com.android.documentsui.ui.DialogController;
40 
41 import com.google.android.material.snackbar.Snackbar;
42 
43 import java.util.function.Predicate;
44 
45 /**
46  * Drag host for items in {@link DirectoryFragment}.
47  */
48 class DragHost<T extends Activity & AbstractActionHandler.CommonAddons> extends AbstractDragHost {
49 
50     private static final String TAG = "dirlist.DragHost";
51 
52     private final T mActivity;
53     private final SelectionTracker<String> mSelectionMgr;
54     private final ActionHandler mActions;
55     private final State mState;
56     private final DialogController mDialogs;
57     private final Predicate<View> mIsDocumentView;
58     private final Lookup<View, DocumentHolder> mHolderLookup;
59     private final Lookup<View, DocumentInfo> mDestinationLookup;
60 
DragHost( T activity, DragAndDropManager dragAndDropManager, SelectionTracker<String> selectionMgr, ActionHandler actions, State state, DialogController dialogs, Predicate<View> isDocumentView, Lookup<View, DocumentHolder> holderLookup, Lookup<View, DocumentInfo> destinationLookup)61     DragHost(
62             T activity,
63             DragAndDropManager dragAndDropManager,
64             SelectionTracker<String> selectionMgr,
65             ActionHandler actions,
66             State state,
67             DialogController dialogs,
68             Predicate<View> isDocumentView,
69             Lookup<View, DocumentHolder> holderLookup,
70             Lookup<View, DocumentInfo> destinationLookup) {
71         super(dragAndDropManager);
72 
73         mActivity = activity;
74         mSelectionMgr = selectionMgr;
75         mActions = actions;
76         mState = state;
77         mDialogs = dialogs;
78         mIsDocumentView = isDocumentView;
79         mHolderLookup = holderLookup;
80         mDestinationLookup = destinationLookup;
81     }
82 
dragStopped(boolean result)83     void dragStopped(boolean result) {
84         if (result) {
85             mSelectionMgr.clearSelection();
86         }
87     }
88 
89     @Override
runOnUiThread(Runnable runnable)90     public void runOnUiThread(Runnable runnable) {
91         mActivity.runOnUiThread(runnable);
92     }
93 
94     @Override
setDropTargetHighlight(View v, boolean highlight)95     public void setDropTargetHighlight(View v, boolean highlight) {
96     }
97 
98     @Override
onViewHovered(View v)99     public void onViewHovered(View v) {
100         if (mIsDocumentView.test(v)) {
101             mActions.springOpenDirectory(mDestinationLookup.lookup(v));
102         }
103         mActivity.setRootsDrawerOpen(false);
104     }
105 
106     @Override
onDragEntered(View v)107     public void onDragEntered(View v) {
108         mActivity.setRootsDrawerOpen(false);
109         mDragAndDropManager.updateState(v, mState.stack.getRoot(), mDestinationLookup.lookup(v));
110     }
111 
112     @Override
canHandleDragEvent(View v)113     public boolean canHandleDragEvent(View v) {
114         boolean dragInitiatedFromDocsUI = mDragAndDropManager.isDragFromSameApp();
115         Metrics.logDragInitiated(dragInitiatedFromDocsUI);
116         if (!dragInitiatedFromDocsUI) {
117             Snackbar.make(
118                     v, R.string.drag_from_another_app, Snackbar.LENGTH_LONG).show();
119             return false;
120         }
121         return true;
122     }
123 
canSpringOpen(View v)124     boolean canSpringOpen(View v) {
125         DocumentInfo doc = mDestinationLookup.lookup(v);
126         return (doc != null) && mDragAndDropManager.canSpringOpen(mState.stack.getRoot(), doc);
127     }
128 
handleDropEvent(View v, DragEvent event)129     boolean handleDropEvent(View v, DragEvent event) {
130         mActivity.setRootsDrawerOpen(false);
131 
132         ClipData clipData = event.getClipData();
133         assert (clipData != null);
134 
135         DocumentInfo dst = mDestinationLookup.lookup(v);
136         if (dst == null) {
137             if (DEBUG) {
138                 Log.d(TAG, "Invalid destination. Ignoring.");
139             }
140             return false;
141         }
142 
143         // If destination is already at top of stack, no need to pass it in
144         DocumentStack dstStack = dst.equals(mState.stack.peek())
145                 ? mState.stack
146                 : new DocumentStack(mState.stack, dst);
147         return mDragAndDropManager.drop(event.getClipData(), event.getLocalState(), dstStack,
148                 mDialogs::showFileOperationStatus);
149     }
150 }
151