Loading res/layout/fragment_directory.xml +10 −64 Original line number Diff line number Diff line Loading @@ -36,58 +36,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- The empty container view --> <FrameLayout android:id="@android:id/empty" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/directory_background" android:focusable="true" android:focusableInTouchMode="true" android:visibility="gone" android:clickable="true"> <LinearLayout android:id="@+id/content" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/artwork" android:src="@drawable/cabinet" android:adjustViewBounds="true" android:layout_height="250dp" android:layout_width="fill_parent" android:alpha="1" android:layout_centerVertical="true" android:layout_marginBottom="25dp" android:scaleType="fitCenter" android:contentDescription="@null"/> <TextView android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/empty" style="@android:style/TextAppearance.Material.Subhead"/> </LinearLayout> </FrameLayout> <LinearLayout android:id="@+id/file_list" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/dir_list" android:layout_width="match_parent" Loading @@ -99,8 +47,6 @@ android:clipToPadding="false" android:scrollbars="none" android:drawSelectorOnTop="true"/> </LinearLayout> </FrameLayout> </com.android.documentsui.dirlist.DocumentsSwipeRefreshLayout> Loading src/com/android/documentsui/dirlist/DirectoryDragListener.java +4 −5 Original line number Diff line number Diff line Loading @@ -19,17 +19,16 @@ package com.android.documentsui.dirlist; import android.view.DragEvent; import android.view.View; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.ItemDragListener; import java.util.TimerTask; import javax.annotation.Nullable; class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { class DirectoryDragListener extends ItemDragListener<DragHost<?>> { DirectoryDragListener(DirectoryFragment fragment) { super(fragment); DirectoryDragListener(com.android.documentsui.dirlist.DragHost<?> host) { super(host); } @Override Loading @@ -55,7 +54,7 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { @Override public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { return DragAndDropHelper.canCopyTo(event.getLocalState(), mDragHost.getDestination(v)) return mDragHost.canCopyTo(event.getLocalState(), v) ? super.createOpenTask(v, event) : null; } } No newline at end of file src/com/android/documentsui/dirlist/DirectoryFragment.java +25 −123 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.app.ActivityManager; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.content.res.Resources; Loading @@ -41,8 +40,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading @@ -55,7 +52,6 @@ import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.Log; import android.util.SparseArray; import android.view.ContextMenu; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; Loading @@ -70,12 +66,10 @@ import com.android.documentsui.BaseActivity; import com.android.documentsui.BaseActivity.RetainedState; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.FocusManager; import com.android.documentsui.Injector; import com.android.documentsui.Injector.ContentScoped; import com.android.documentsui.Injector.Injected; import com.android.documentsui.ItemDragListener; import com.android.documentsui.Metrics; import com.android.documentsui.Model; import com.android.documentsui.R; Loading Loading @@ -118,8 +112,7 @@ import javax.annotation.Nullable; /** * Display the documents inside a single directory. */ public class DirectoryFragment extends Fragment implements ItemDragListener.DragHost, SwipeRefreshLayout.OnRefreshListener { public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { static final int TYPE_NORMAL = 1; static final int TYPE_RECENT_OPEN = 2; Loading Loading @@ -171,7 +164,6 @@ public class DirectoryFragment extends Fragment private IconHelper mIconHelper; private SwipeRefreshLayout mRefreshLayout; private RecyclerView mRecView; private View mFileList; private DocumentsAdapter mAdapter; private DocumentClipper mClipper; Loading Loading @@ -203,7 +195,7 @@ public class DirectoryFragment extends Fragment public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { BaseActivity activity = (BaseActivity) getActivity(); mActivity = (BaseActivity) getActivity(); final View view = inflater.inflate(R.layout.fragment_directory, container, false); mProgressBar = view.findViewById(R.id.progressbar); Loading Loading @@ -231,19 +223,33 @@ public class DirectoryFragment extends Fragment resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range), resources.getDimensionPixelOffset(R.dimen.fastscroll_margin) ); mRecView.setItemAnimator(new DirectoryItemAnimator(activity)); mFileList = view.findViewById(R.id.file_list); mRecView.setItemAnimator(new DirectoryItemAnimator(mActivity)); mInjector = activity.getInjector(); mInjector = mActivity.getInjector(); mModel = mInjector.getModel(); mModel.reset(); mInjector.actions.registerDisplayStateChangedListener(mOnDisplayStateChanged); mDragHoverListener = mInjector.config.dragAndDropEnabled() ? DragHoverListener.create(new DirectoryDragListener(this), mRecView) : null; mClipper = DocumentsApplication.getDocumentClipper(getContext()); if (mInjector.config.dragAndDropEnabled()) { DirectoryDragListener listener = new DirectoryDragListener( new DragHost<>( mActivity, mActivity.getShadowBuilder(), mInjector.selectionMgr, mInjector.actions, mActivity.getDisplayState(), mInjector.dialogs, (View v) -> { return getModelId(v) != null; }, this::getDocumentHolder, this::getDestination, mClipper )); mDragHoverListener = DragHoverListener.create(listener, mRecView); } // Make the recycler and the empty views responsive to drop events when allowed. mRecView.setOnDragListener(mDragHoverListener); Loading Loading @@ -272,7 +278,6 @@ public class DirectoryFragment extends Fragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mActivity = (BaseActivity) getActivity(); mState = mActivity.getDisplayState(); // Read arguments when object created for the first time. Loading @@ -292,7 +297,6 @@ public class DirectoryFragment extends Fragment } mIconHelper = new IconHelper(mActivity, MODE_GRID); mClipper = DocumentsApplication.getDocumentClipper(getContext()); mAdapter = new DirectoryAddonsAdapter( mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper)); Loading Loading @@ -908,92 +912,7 @@ public class DirectoryFragment extends Fragment } } void dragStopped(boolean result) { if (result) { mSelectionMgr.clearSelection(); } } @Override public void runOnUiThread(Runnable runnable) { getActivity().runOnUiThread(runnable); } // In DirectoryFragment, we close the roots drawer right away. // We also want to update the Drag Shadow to indicate whether the // item is droppable or not. @Override public void onDragEntered(View v, Object localState) { mActivity.setRootsDrawerOpen(false); mActivity.getShadowBuilder() .setAppearDroppable(DragAndDropHelper.canCopyTo(localState, getDestination(v))); v.updateDragShadow(mActivity.getShadowBuilder()); } // In DirectoryFragment, we always reset the background of the Drag Shadow once it // exits. @Override public void onDragExited(View v, Object localState) { mActivity.getShadowBuilder().resetBackground(); v.updateDragShadow(mActivity.getShadowBuilder()); if (v.getParent() == mRecView) { DocumentHolder holder = getDocumentHolder(v); if (holder != null) { holder.resetDropHighlight(); } } } // In DirectoryFragment, we spring loads the hovered folder. @Override public void onViewHovered(View view) { BaseActivity activity = mActivity; if (getModelId(view) != null) { mActions.springOpenDirectory(getDestination(view)); } activity.setRootsDrawerOpen(false); } boolean handleDropEvent(View v, DragEvent event) { BaseActivity activity = (BaseActivity) getActivity(); activity.setRootsDrawerOpen(false); ClipData clipData = event.getClipData(); assert (clipData != null); assert(mClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); if (!DragAndDropHelper.canCopyTo(event.getLocalState(), getDestination(v))) { return false; } // Recognize multi-window drag and drop based on the fact that localState is not // carried between processes. It will stop working when the localsState behavior // is changed. The info about window should be passed in the localState then. // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(getContext(), event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = getDestination(v); // If destination is already at top of stack, no need to pass it in if (dst.equals(mState.stack.peek())) { mClipper.copyFromClipData( mState.stack, clipData, mInjector.dialogs::showFileOperationStatus); } else { mClipper.copyFromClipData( dst, mState.stack, clipData, mInjector.dialogs::showFileOperationStatus); } return true; } DocumentInfo getDestination(View v) { private DocumentInfo getDestination(View v) { String id = getModelId(v); if (id != null) { Cursor dstCursor = mModel.getItem(id); Loading @@ -1011,30 +930,13 @@ public class DirectoryFragment extends Fragment return null; } @Override public void setDropTargetHighlight(View v, Object localState, boolean highlight) { // Note: use exact comparison - this code is searching for views which are children of // the RecyclerView instance in the UI. if (v.getParent() == mRecView) { DocumentHolder holder = getDocumentHolder(v); if (holder != null) { if (!highlight) { holder.resetDropHighlight(); } else { holder.setDroppableHighlight( DragAndDropHelper.canCopyTo(localState, getDestination(v))); } } } } /** * Gets the model ID for a given RecyclerView item. * @param view A View that is a document item view, or a child of a document item view. * @return The Model ID for the given document, or null if the given view is not associated with * a document item view. */ protected @Nullable String getModelId(View view) { private @Nullable String getModelId(View view) { View itemView = mRecView.findContainingItemView(view); if (itemView != null) { RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView); Loading src/com/android/documentsui/dirlist/DragHost.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.dirlist; import android.app.Activity; import android.content.ClipData; import android.view.DragEvent; import android.view.View; import com.android.documentsui.AbstractActionHandler; import com.android.documentsui.ActionHandler; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.DragShadowBuilder; import com.android.documentsui.ItemDragListener; import com.android.documentsui.Metrics; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Lookup; import com.android.documentsui.base.State; import com.android.documentsui.clipping.DocumentClipper; import com.android.documentsui.selection.SelectionManager; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.ui.DialogController; import java.util.function.Predicate; /** * Drag host for items in {@link DirectoryFragment}. */ class DragHost<T extends Activity & AbstractActionHandler.CommonAddons> implements ItemDragListener.DragHost { private final T mActivity; private final DragShadowBuilder mShadowBuilder; private final SelectionManager mSelectionMgr; private final ActionHandler mActions; private final State mState; private final DialogController mDialogs; private final Predicate<View> mIsDocumentView; private final Lookup<View, DocumentHolder> mHolderLookup; private final Lookup<View, DocumentInfo> mDestinationLookup; private final DocumentClipper mClipper; DragHost( T activity, DragShadowBuilder shadowBuilder, SelectionManager selectionMgr, ActionHandler actions, State state, DialogController dialogs, Predicate<View> isDocumentView, Lookup<View, DocumentHolder> holderLookup, Lookup<View, DocumentInfo> destinationLookup, DocumentClipper clipper) { mActivity = activity; mShadowBuilder = shadowBuilder; mSelectionMgr = selectionMgr; mActions = actions; mState = state; mDialogs = dialogs; mIsDocumentView = isDocumentView; mHolderLookup = holderLookup; mDestinationLookup = destinationLookup; mClipper = clipper; } void dragStopped(boolean result) { if (result) { mSelectionMgr.clearSelection(); } } @Override public void runOnUiThread(Runnable runnable) { mActivity.runOnUiThread(runnable); } @Override public void setDropTargetHighlight(View v, Object localState, boolean highlight) { // Note: use exact comparison - this code is searching for views which are children of // the RecyclerView instance in the UI. if (mIsDocumentView.test(v)) { DocumentHolder holder = mHolderLookup.lookup(v); if (holder != null) { if (!highlight) { holder.resetDropHighlight(); } else { holder.setDroppableHighlight(canCopyTo(localState, v)); } } } } @Override public void onViewHovered(View v) { if (mIsDocumentView.test(v)) { mActions.springOpenDirectory(mDestinationLookup.lookup(v)); } mActivity.setRootsDrawerOpen(false); } @Override public void onDragEntered(View v, Object localState) { mActivity.setRootsDrawerOpen(false); mShadowBuilder.setAppearDroppable(canCopyTo(localState, v)); v.updateDragShadow(mShadowBuilder); } @Override public void onDragExited(View v, Object localState) { mShadowBuilder.resetBackground(); v.updateDragShadow(mShadowBuilder); if (mIsDocumentView.test(v)) { DocumentHolder holder = mHolderLookup.lookup(v); if (holder != null) { holder.resetDropHighlight(); } } } boolean handleDropEvent(View v, DragEvent event) { mActivity.setRootsDrawerOpen(false); ClipData clipData = event.getClipData(); assert (clipData != null); assert(mClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); if (!canCopyTo(event.getLocalState(), v)) { return false; } // Recognize multi-window drag and drop based on the fact that localState is not // carried between processes. It will stop working when the localsState behavior // is changed. The info about window should be passed in the localState then. // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(mActivity, event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = mDestinationLookup.lookup(v); // If destination is already at top of stack, no need to pass it in if (dst.equals(mState.stack.peek())) { mClipper.copyFromClipData( mState.stack, clipData, mDialogs::showFileOperationStatus); } else { mClipper.copyFromClipData( dst, mState.stack, clipData, mDialogs::showFileOperationStatus); } return true; } boolean canCopyTo(Object localState, View v) { return DragAndDropHelper.canCopyTo(localState, mDestinationLookup.lookup(v)); } } src/com/android/documentsui/sidebar/AppItem.java +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class AppItem extends Item { } @Override boolean isDropTarget() { boolean isRoot() { // We won't support drag n' drop in pickers, and apps only show up there. return false; } Loading Loading
res/layout/fragment_directory.xml +10 −64 Original line number Diff line number Diff line Loading @@ -36,58 +36,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- The empty container view --> <FrameLayout android:id="@android:id/empty" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/directory_background" android:focusable="true" android:focusableInTouchMode="true" android:visibility="gone" android:clickable="true"> <LinearLayout android:id="@+id/content" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/artwork" android:src="@drawable/cabinet" android:adjustViewBounds="true" android:layout_height="250dp" android:layout_width="fill_parent" android:alpha="1" android:layout_centerVertical="true" android:layout_marginBottom="25dp" android:scaleType="fitCenter" android:contentDescription="@null"/> <TextView android:id="@+id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/empty" style="@android:style/TextAppearance.Material.Subhead"/> </LinearLayout> </FrameLayout> <LinearLayout android:id="@+id/file_list" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/dir_list" android:layout_width="match_parent" Loading @@ -99,8 +47,6 @@ android:clipToPadding="false" android:scrollbars="none" android:drawSelectorOnTop="true"/> </LinearLayout> </FrameLayout> </com.android.documentsui.dirlist.DocumentsSwipeRefreshLayout> Loading
src/com/android/documentsui/dirlist/DirectoryDragListener.java +4 −5 Original line number Diff line number Diff line Loading @@ -19,17 +19,16 @@ package com.android.documentsui.dirlist; import android.view.DragEvent; import android.view.View; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.ItemDragListener; import java.util.TimerTask; import javax.annotation.Nullable; class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { class DirectoryDragListener extends ItemDragListener<DragHost<?>> { DirectoryDragListener(DirectoryFragment fragment) { super(fragment); DirectoryDragListener(com.android.documentsui.dirlist.DragHost<?> host) { super(host); } @Override Loading @@ -55,7 +54,7 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { @Override public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { return DragAndDropHelper.canCopyTo(event.getLocalState(), mDragHost.getDestination(v)) return mDragHost.canCopyTo(event.getLocalState(), v) ? super.createOpenTask(v, event) : null; } } No newline at end of file
src/com/android/documentsui/dirlist/DirectoryFragment.java +25 −123 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.app.ActivityManager; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.content.res.Resources; Loading @@ -41,8 +40,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; Loading @@ -55,7 +52,6 @@ import android.support.v7.widget.RecyclerView.ViewHolder; import android.util.Log; import android.util.SparseArray; import android.view.ContextMenu; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; Loading @@ -70,12 +66,10 @@ import com.android.documentsui.BaseActivity; import com.android.documentsui.BaseActivity.RetainedState; import com.android.documentsui.DirectoryReloadLock; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.FocusManager; import com.android.documentsui.Injector; import com.android.documentsui.Injector.ContentScoped; import com.android.documentsui.Injector.Injected; import com.android.documentsui.ItemDragListener; import com.android.documentsui.Metrics; import com.android.documentsui.Model; import com.android.documentsui.R; Loading Loading @@ -118,8 +112,7 @@ import javax.annotation.Nullable; /** * Display the documents inside a single directory. */ public class DirectoryFragment extends Fragment implements ItemDragListener.DragHost, SwipeRefreshLayout.OnRefreshListener { public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { static final int TYPE_NORMAL = 1; static final int TYPE_RECENT_OPEN = 2; Loading Loading @@ -171,7 +164,6 @@ public class DirectoryFragment extends Fragment private IconHelper mIconHelper; private SwipeRefreshLayout mRefreshLayout; private RecyclerView mRecView; private View mFileList; private DocumentsAdapter mAdapter; private DocumentClipper mClipper; Loading Loading @@ -203,7 +195,7 @@ public class DirectoryFragment extends Fragment public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { BaseActivity activity = (BaseActivity) getActivity(); mActivity = (BaseActivity) getActivity(); final View view = inflater.inflate(R.layout.fragment_directory, container, false); mProgressBar = view.findViewById(R.id.progressbar); Loading Loading @@ -231,19 +223,33 @@ public class DirectoryFragment extends Fragment resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range), resources.getDimensionPixelOffset(R.dimen.fastscroll_margin) ); mRecView.setItemAnimator(new DirectoryItemAnimator(activity)); mFileList = view.findViewById(R.id.file_list); mRecView.setItemAnimator(new DirectoryItemAnimator(mActivity)); mInjector = activity.getInjector(); mInjector = mActivity.getInjector(); mModel = mInjector.getModel(); mModel.reset(); mInjector.actions.registerDisplayStateChangedListener(mOnDisplayStateChanged); mDragHoverListener = mInjector.config.dragAndDropEnabled() ? DragHoverListener.create(new DirectoryDragListener(this), mRecView) : null; mClipper = DocumentsApplication.getDocumentClipper(getContext()); if (mInjector.config.dragAndDropEnabled()) { DirectoryDragListener listener = new DirectoryDragListener( new DragHost<>( mActivity, mActivity.getShadowBuilder(), mInjector.selectionMgr, mInjector.actions, mActivity.getDisplayState(), mInjector.dialogs, (View v) -> { return getModelId(v) != null; }, this::getDocumentHolder, this::getDestination, mClipper )); mDragHoverListener = DragHoverListener.create(listener, mRecView); } // Make the recycler and the empty views responsive to drop events when allowed. mRecView.setOnDragListener(mDragHoverListener); Loading Loading @@ -272,7 +278,6 @@ public class DirectoryFragment extends Fragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mActivity = (BaseActivity) getActivity(); mState = mActivity.getDisplayState(); // Read arguments when object created for the first time. Loading @@ -292,7 +297,6 @@ public class DirectoryFragment extends Fragment } mIconHelper = new IconHelper(mActivity, MODE_GRID); mClipper = DocumentsApplication.getDocumentClipper(getContext()); mAdapter = new DirectoryAddonsAdapter( mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper)); Loading Loading @@ -908,92 +912,7 @@ public class DirectoryFragment extends Fragment } } void dragStopped(boolean result) { if (result) { mSelectionMgr.clearSelection(); } } @Override public void runOnUiThread(Runnable runnable) { getActivity().runOnUiThread(runnable); } // In DirectoryFragment, we close the roots drawer right away. // We also want to update the Drag Shadow to indicate whether the // item is droppable or not. @Override public void onDragEntered(View v, Object localState) { mActivity.setRootsDrawerOpen(false); mActivity.getShadowBuilder() .setAppearDroppable(DragAndDropHelper.canCopyTo(localState, getDestination(v))); v.updateDragShadow(mActivity.getShadowBuilder()); } // In DirectoryFragment, we always reset the background of the Drag Shadow once it // exits. @Override public void onDragExited(View v, Object localState) { mActivity.getShadowBuilder().resetBackground(); v.updateDragShadow(mActivity.getShadowBuilder()); if (v.getParent() == mRecView) { DocumentHolder holder = getDocumentHolder(v); if (holder != null) { holder.resetDropHighlight(); } } } // In DirectoryFragment, we spring loads the hovered folder. @Override public void onViewHovered(View view) { BaseActivity activity = mActivity; if (getModelId(view) != null) { mActions.springOpenDirectory(getDestination(view)); } activity.setRootsDrawerOpen(false); } boolean handleDropEvent(View v, DragEvent event) { BaseActivity activity = (BaseActivity) getActivity(); activity.setRootsDrawerOpen(false); ClipData clipData = event.getClipData(); assert (clipData != null); assert(mClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); if (!DragAndDropHelper.canCopyTo(event.getLocalState(), getDestination(v))) { return false; } // Recognize multi-window drag and drop based on the fact that localState is not // carried between processes. It will stop working when the localsState behavior // is changed. The info about window should be passed in the localState then. // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(getContext(), event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = getDestination(v); // If destination is already at top of stack, no need to pass it in if (dst.equals(mState.stack.peek())) { mClipper.copyFromClipData( mState.stack, clipData, mInjector.dialogs::showFileOperationStatus); } else { mClipper.copyFromClipData( dst, mState.stack, clipData, mInjector.dialogs::showFileOperationStatus); } return true; } DocumentInfo getDestination(View v) { private DocumentInfo getDestination(View v) { String id = getModelId(v); if (id != null) { Cursor dstCursor = mModel.getItem(id); Loading @@ -1011,30 +930,13 @@ public class DirectoryFragment extends Fragment return null; } @Override public void setDropTargetHighlight(View v, Object localState, boolean highlight) { // Note: use exact comparison - this code is searching for views which are children of // the RecyclerView instance in the UI. if (v.getParent() == mRecView) { DocumentHolder holder = getDocumentHolder(v); if (holder != null) { if (!highlight) { holder.resetDropHighlight(); } else { holder.setDroppableHighlight( DragAndDropHelper.canCopyTo(localState, getDestination(v))); } } } } /** * Gets the model ID for a given RecyclerView item. * @param view A View that is a document item view, or a child of a document item view. * @return The Model ID for the given document, or null if the given view is not associated with * a document item view. */ protected @Nullable String getModelId(View view) { private @Nullable String getModelId(View view) { View itemView = mRecView.findContainingItemView(view); if (itemView != null) { RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView); Loading
src/com/android/documentsui/dirlist/DragHost.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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.dirlist; import android.app.Activity; import android.content.ClipData; import android.view.DragEvent; import android.view.View; import com.android.documentsui.AbstractActionHandler; import com.android.documentsui.ActionHandler; import com.android.documentsui.DragAndDropHelper; import com.android.documentsui.DragShadowBuilder; import com.android.documentsui.ItemDragListener; import com.android.documentsui.Metrics; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.Lookup; import com.android.documentsui.base.State; import com.android.documentsui.clipping.DocumentClipper; import com.android.documentsui.selection.SelectionManager; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.ui.DialogController; import java.util.function.Predicate; /** * Drag host for items in {@link DirectoryFragment}. */ class DragHost<T extends Activity & AbstractActionHandler.CommonAddons> implements ItemDragListener.DragHost { private final T mActivity; private final DragShadowBuilder mShadowBuilder; private final SelectionManager mSelectionMgr; private final ActionHandler mActions; private final State mState; private final DialogController mDialogs; private final Predicate<View> mIsDocumentView; private final Lookup<View, DocumentHolder> mHolderLookup; private final Lookup<View, DocumentInfo> mDestinationLookup; private final DocumentClipper mClipper; DragHost( T activity, DragShadowBuilder shadowBuilder, SelectionManager selectionMgr, ActionHandler actions, State state, DialogController dialogs, Predicate<View> isDocumentView, Lookup<View, DocumentHolder> holderLookup, Lookup<View, DocumentInfo> destinationLookup, DocumentClipper clipper) { mActivity = activity; mShadowBuilder = shadowBuilder; mSelectionMgr = selectionMgr; mActions = actions; mState = state; mDialogs = dialogs; mIsDocumentView = isDocumentView; mHolderLookup = holderLookup; mDestinationLookup = destinationLookup; mClipper = clipper; } void dragStopped(boolean result) { if (result) { mSelectionMgr.clearSelection(); } } @Override public void runOnUiThread(Runnable runnable) { mActivity.runOnUiThread(runnable); } @Override public void setDropTargetHighlight(View v, Object localState, boolean highlight) { // Note: use exact comparison - this code is searching for views which are children of // the RecyclerView instance in the UI. if (mIsDocumentView.test(v)) { DocumentHolder holder = mHolderLookup.lookup(v); if (holder != null) { if (!highlight) { holder.resetDropHighlight(); } else { holder.setDroppableHighlight(canCopyTo(localState, v)); } } } } @Override public void onViewHovered(View v) { if (mIsDocumentView.test(v)) { mActions.springOpenDirectory(mDestinationLookup.lookup(v)); } mActivity.setRootsDrawerOpen(false); } @Override public void onDragEntered(View v, Object localState) { mActivity.setRootsDrawerOpen(false); mShadowBuilder.setAppearDroppable(canCopyTo(localState, v)); v.updateDragShadow(mShadowBuilder); } @Override public void onDragExited(View v, Object localState) { mShadowBuilder.resetBackground(); v.updateDragShadow(mShadowBuilder); if (mIsDocumentView.test(v)) { DocumentHolder holder = mHolderLookup.lookup(v); if (holder != null) { holder.resetDropHighlight(); } } } boolean handleDropEvent(View v, DragEvent event) { mActivity.setRootsDrawerOpen(false); ClipData clipData = event.getClipData(); assert (clipData != null); assert(mClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); if (!canCopyTo(event.getLocalState(), v)) { return false; } // Recognize multi-window drag and drop based on the fact that localState is not // carried between processes. It will stop working when the localsState behavior // is changed. The info about window should be passed in the localState then. // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(mActivity, event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = mDestinationLookup.lookup(v); // If destination is already at top of stack, no need to pass it in if (dst.equals(mState.stack.peek())) { mClipper.copyFromClipData( mState.stack, clipData, mDialogs::showFileOperationStatus); } else { mClipper.copyFromClipData( dst, mState.stack, clipData, mDialogs::showFileOperationStatus); } return true; } boolean canCopyTo(Object localState, View v) { return DragAndDropHelper.canCopyTo(localState, mDestinationLookup.lookup(v)); } }
src/com/android/documentsui/sidebar/AppItem.java +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class AppItem extends Item { } @Override boolean isDropTarget() { boolean isRoot() { // We won't support drag n' drop in pickers, and apps only show up there. return false; } Loading