Loading packages/DocumentsUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,6 @@ <dimen name="dir_elevation">8dp</dimen> <dimen name="drag_shadow_size">120dp</dimen> </resources> packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +140 −7 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; Loading @@ -62,6 +63,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; Loading @@ -88,6 +90,7 @@ import com.android.documentsui.model.RootInfo; import com.google.android.collect.Lists; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** Loading Loading @@ -202,6 +205,7 @@ public class DirectoryFragment extends Fragment { mListView.setOnItemClickListener(mItemListener); mListView.setMultiChoiceModeListener(mMultiListener); mListView.setRecyclerListener(mRecycleListener); setupDragAndDropOnDirectoryView(mListView); // Indent our list divider to align with text final Drawable divider = mListView.getDivider(); Loading @@ -217,6 +221,7 @@ public class DirectoryFragment extends Fragment { mGridView.setOnItemClickListener(mItemListener); mGridView.setMultiChoiceModeListener(mMultiListener); mGridView.setRecyclerListener(mRecycleListener); setupDragAndDropOnDirectoryView(mGridView); return view; } Loading Loading @@ -897,13 +902,8 @@ public class DirectoryFragment extends Fragment { iconMime.setAlpha(1f); iconThumb.setAlpha(0f); iconThumb.setImageDrawable(null); if (docIcon != 0) { iconMime.setImageDrawable( IconUtils.loadPackageIcon(context, docAuthority, docIcon)); } else { iconMime.setImageDrawable(IconUtils.loadMimeIcon( context, docMimeType, docAuthority, docId, state.derivedMode)); } getDocumentIcon(context, docAuthority, docId, docMimeType, docIcon, state)); } boolean hasLine1 = false; Loading Loading @@ -1011,6 +1011,8 @@ public class DirectoryFragment extends Fragment { if (icon1 != null) icon1.setAlpha(iconAlpha); if (icon2 != null) icon2.setAlpha(iconAlpha); setupDragAndDropOnDocumentView(convertView, cursor); return convertView; } Loading Loading @@ -1262,4 +1264,135 @@ public class DirectoryFragment extends Fragment { } return clipData; } private void setupDragAndDropOnDirectoryView(AbsListView view) { // Listen for drops on non-directory items and empty space. view.setOnDragListener(mOnDragListener); } private void setupDragAndDropOnDocumentView(View view, Cursor cursor) { final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); if (Document.MIME_TYPE_DIR.equals(docMimeType)) { // Make a directory item a drop target. Drop on non-directories and empty space // is handled at the list/grid view level. view.setOnDragListener(mOnDragListener); } // Temporary: attaching the listener to the title only. // Attaching to the entire item conflicts with the item long click handler responsible // for item selection. final View title = view.findViewById(android.R.id.title); title.setOnLongClickListener(mLongClickListener); } private View.OnDragListener mOnDragListener = new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: // TODO: Check if the event contains droppable data. return true; // TODO: Highlight potential drop target directory? // TODO: Expand drop target directory on hover? case DragEvent.ACTION_DRAG_ENTERED: case DragEvent.ACTION_DRAG_LOCATION: case DragEvent.ACTION_DRAG_EXITED: case DragEvent.ACTION_DRAG_ENDED: return true; case DragEvent.ACTION_DROP: int dstPosition = mCurrentView.getPositionForView(v); DocumentInfo dstDir = null; if (dstPosition != android.widget.AdapterView.INVALID_POSITION) { Cursor dstCursor = mAdapter.getItem(dstPosition); dstDir = DocumentInfo.fromDirectoryCursor(dstCursor); // TODO: Do not drop into the directory where the documents came from. } copyFromClipData(event.getClipData(), dstDir); return true; } return false; } }; private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { final List<DocumentInfo> docs = getDraggableDocuments(v); if (docs.isEmpty()) { return false; } v.startDrag( getClipDataFromDocuments(docs), new DrawableShadowBuilder(getDragShadowIcon(docs)), null, View.DRAG_FLAG_GLOBAL ); return true; } }; private List<DocumentInfo> getDraggableDocuments(View currentItemView) { final int position = mCurrentView.getPositionForView(currentItemView); if (position == android.widget.AdapterView.INVALID_POSITION) { return Collections.EMPTY_LIST; } final List<DocumentInfo> selectedDocs = getSelectedDocuments(); if (!selectedDocs.isEmpty()) { if (!mCurrentView.isItemChecked(position)) { // There is a selection that does not include the current item, drag nothing. return Collections.EMPTY_LIST; } return selectedDocs; } final Cursor cursor = mAdapter.getItem(position); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); return Lists.newArrayList(doc); } private Drawable getDragShadowIcon(List<DocumentInfo> docs) { if (docs.size() == 1) { final DocumentInfo doc = docs.get(0); return getDocumentIcon(getActivity(), doc.authority, doc.documentId, doc.mimeType, doc.icon, getDisplayState(this)); } return getActivity().getDrawable(R.drawable.ic_doc_generic); } public static Drawable getDocumentIcon(Context context, String docAuthority, String docId, String docMimeType, int docIcon, State state) { if (docIcon != 0) { return IconUtils.loadPackageIcon(context, docAuthority, docIcon); } else { return IconUtils.loadMimeIcon(context, docMimeType, docAuthority, docId, state.derivedMode); } } private class DrawableShadowBuilder extends View.DragShadowBuilder { private final Drawable mShadow; private final int mShadowDimension; public DrawableShadowBuilder(Drawable shadow) { mShadow = shadow; mShadowDimension = getResources().getDimensionPixelSize( R.dimen.drag_shadow_size); mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension); } public void onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint) { shadowSize.set(mShadowDimension, mShadowDimension); shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2); } public void onDrawShadow(Canvas canvas) { mShadow.draw(canvas); } } } Loading
packages/DocumentsUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,6 @@ <dimen name="dir_elevation">8dp</dimen> <dimen name="drag_shadow_size">120dp</dimen> </resources>
packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +140 −7 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; Loading @@ -62,6 +63,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; Loading @@ -88,6 +90,7 @@ import com.android.documentsui.model.RootInfo; import com.google.android.collect.Lists; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** Loading Loading @@ -202,6 +205,7 @@ public class DirectoryFragment extends Fragment { mListView.setOnItemClickListener(mItemListener); mListView.setMultiChoiceModeListener(mMultiListener); mListView.setRecyclerListener(mRecycleListener); setupDragAndDropOnDirectoryView(mListView); // Indent our list divider to align with text final Drawable divider = mListView.getDivider(); Loading @@ -217,6 +221,7 @@ public class DirectoryFragment extends Fragment { mGridView.setOnItemClickListener(mItemListener); mGridView.setMultiChoiceModeListener(mMultiListener); mGridView.setRecyclerListener(mRecycleListener); setupDragAndDropOnDirectoryView(mGridView); return view; } Loading Loading @@ -897,13 +902,8 @@ public class DirectoryFragment extends Fragment { iconMime.setAlpha(1f); iconThumb.setAlpha(0f); iconThumb.setImageDrawable(null); if (docIcon != 0) { iconMime.setImageDrawable( IconUtils.loadPackageIcon(context, docAuthority, docIcon)); } else { iconMime.setImageDrawable(IconUtils.loadMimeIcon( context, docMimeType, docAuthority, docId, state.derivedMode)); } getDocumentIcon(context, docAuthority, docId, docMimeType, docIcon, state)); } boolean hasLine1 = false; Loading Loading @@ -1011,6 +1011,8 @@ public class DirectoryFragment extends Fragment { if (icon1 != null) icon1.setAlpha(iconAlpha); if (icon2 != null) icon2.setAlpha(iconAlpha); setupDragAndDropOnDocumentView(convertView, cursor); return convertView; } Loading Loading @@ -1262,4 +1264,135 @@ public class DirectoryFragment extends Fragment { } return clipData; } private void setupDragAndDropOnDirectoryView(AbsListView view) { // Listen for drops on non-directory items and empty space. view.setOnDragListener(mOnDragListener); } private void setupDragAndDropOnDocumentView(View view, Cursor cursor) { final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); if (Document.MIME_TYPE_DIR.equals(docMimeType)) { // Make a directory item a drop target. Drop on non-directories and empty space // is handled at the list/grid view level. view.setOnDragListener(mOnDragListener); } // Temporary: attaching the listener to the title only. // Attaching to the entire item conflicts with the item long click handler responsible // for item selection. final View title = view.findViewById(android.R.id.title); title.setOnLongClickListener(mLongClickListener); } private View.OnDragListener mOnDragListener = new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: // TODO: Check if the event contains droppable data. return true; // TODO: Highlight potential drop target directory? // TODO: Expand drop target directory on hover? case DragEvent.ACTION_DRAG_ENTERED: case DragEvent.ACTION_DRAG_LOCATION: case DragEvent.ACTION_DRAG_EXITED: case DragEvent.ACTION_DRAG_ENDED: return true; case DragEvent.ACTION_DROP: int dstPosition = mCurrentView.getPositionForView(v); DocumentInfo dstDir = null; if (dstPosition != android.widget.AdapterView.INVALID_POSITION) { Cursor dstCursor = mAdapter.getItem(dstPosition); dstDir = DocumentInfo.fromDirectoryCursor(dstCursor); // TODO: Do not drop into the directory where the documents came from. } copyFromClipData(event.getClipData(), dstDir); return true; } return false; } }; private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { final List<DocumentInfo> docs = getDraggableDocuments(v); if (docs.isEmpty()) { return false; } v.startDrag( getClipDataFromDocuments(docs), new DrawableShadowBuilder(getDragShadowIcon(docs)), null, View.DRAG_FLAG_GLOBAL ); return true; } }; private List<DocumentInfo> getDraggableDocuments(View currentItemView) { final int position = mCurrentView.getPositionForView(currentItemView); if (position == android.widget.AdapterView.INVALID_POSITION) { return Collections.EMPTY_LIST; } final List<DocumentInfo> selectedDocs = getSelectedDocuments(); if (!selectedDocs.isEmpty()) { if (!mCurrentView.isItemChecked(position)) { // There is a selection that does not include the current item, drag nothing. return Collections.EMPTY_LIST; } return selectedDocs; } final Cursor cursor = mAdapter.getItem(position); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); return Lists.newArrayList(doc); } private Drawable getDragShadowIcon(List<DocumentInfo> docs) { if (docs.size() == 1) { final DocumentInfo doc = docs.get(0); return getDocumentIcon(getActivity(), doc.authority, doc.documentId, doc.mimeType, doc.icon, getDisplayState(this)); } return getActivity().getDrawable(R.drawable.ic_doc_generic); } public static Drawable getDocumentIcon(Context context, String docAuthority, String docId, String docMimeType, int docIcon, State state) { if (docIcon != 0) { return IconUtils.loadPackageIcon(context, docAuthority, docIcon); } else { return IconUtils.loadMimeIcon(context, docMimeType, docAuthority, docId, state.derivedMode); } } private class DrawableShadowBuilder extends View.DragShadowBuilder { private final Drawable mShadow; private final int mShadowDimension; public DrawableShadowBuilder(Drawable shadow) { mShadow = shadow; mShadowDimension = getResources().getDimensionPixelSize( R.dimen.drag_shadow_size); mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension); } public void onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint) { shadowSize.set(mShadowDimension, mShadowDimension); shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2); } public void onDrawShadow(Canvas canvas) { mShadow.draw(canvas); } } }