Loading packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +185 −107 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.Parcelable; import android.os.SystemProperties; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.support.annotation.Nullable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; Loading Loading @@ -96,7 +97,6 @@ import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; import com.google.common.collect.Lists; import java.util.ArrayList; Loading Loading @@ -129,6 +129,8 @@ public class DirectoryFragment extends Fragment { private static final String EXTRA_QUERY = "query"; private static final String EXTRA_IGNORE_STATE = "ignoreState"; private final Model mModel = new Model(); private final Handler mHandler = new Handler(Looper.getMainLooper()); private View mEmptyView; Loading @@ -147,7 +149,6 @@ public class DirectoryFragment extends Fragment { private LoaderCallbacks<DirectoryResult> mCallbacks; private FragmentTuner mFragmentTuner; private DocumentClipper mClipper; private MultiSelectManager mSelectionManager; // These are lazily initialized. private LinearLayoutManager mListLayout; private GridLayoutManager mGridLayout; Loading Loading @@ -261,7 +262,7 @@ public class DirectoryFragment extends Fragment { } // Clear any outstanding selection mSelectionManager.clearSelection(); mModel.clearSelection(); } @Override Loading Loading @@ -290,14 +291,17 @@ public class DirectoryFragment extends Fragment { } }; mSelectionManager = new MultiSelectManager( // TODO: instead of inserting the view into the constructor, extract listener-creation code // and set the listener on the view after the fact. Then the view doesn't need to be passed // into the selection manager which is passed into the model. MultiSelectManager selMgr= new MultiSelectManager( mRecView, listener, state.allowMultiple ? MultiSelectManager.MODE_MULTIPLE : MultiSelectManager.MODE_SINGLE); mSelectionManager.addCallback(new SelectionModeListener()); selMgr.addCallback(new SelectionModeListener()); mModel.setSelectionManager(selMgr); mType = getArguments().getInt(EXTRA_TYPE); mStateKey = buildStateKey(root, doc); Loading Loading @@ -367,7 +371,9 @@ public class DirectoryFragment extends Fragment { if (!isAdded()) return; mAdapter.replaceResult(result); // TODO: make the adapter listen to the model mModel.update(result); mAdapter.update(); // Push latest state up to UI // TODO: if mode change was racing with us, don't overwrite it Loading @@ -380,7 +386,7 @@ public class DirectoryFragment extends Fragment { updateDisplayState(); // When launched into empty recents, show drawer if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched && if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched && context instanceof DocumentsActivity) { ((DocumentsActivity) context).setRootsDrawerOpen(true); } Loading @@ -398,7 +404,9 @@ public class DirectoryFragment extends Fragment { @Override public void onLoaderReset(Loader<DirectoryResult> loader) { mAdapter.replaceResult(null); // TODO: make the adapter listen to the model. mModel.update(null); mAdapter.update(); } }; Loading Loading @@ -433,7 +441,7 @@ public class DirectoryFragment extends Fragment { } private boolean onSingleTapUp(MotionEvent e) { if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) { if (Events.isTouchEvent(e) && mModel.getSelection().isEmpty()) { int position = getEventAdapterPosition(e); if (position != RecyclerView.NO_POSITION) { return handleViewItem(position); Loading @@ -454,14 +462,14 @@ public class DirectoryFragment extends Fragment { } private boolean handleViewItem(int position) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (isDocumentEnabled(docMimeType, docFlags)) { final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); ((BaseActivity) getActivity()).onDocumentPicked(doc, mAdapter); mSelectionManager.clearSelection(); ((BaseActivity) getActivity()).onDocumentPicked(doc, mModel); mModel.clearSelection(); return true; } return false; Loading Loading @@ -598,7 +606,7 @@ public class DirectoryFragment extends Fragment { public boolean onBeforeItemStateChange(int position, boolean selected) { // Directories and footer items cannot be checked if (selected) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); Loading @@ -610,7 +618,7 @@ public class DirectoryFragment extends Fragment { @Override public void onItemStateChanged(int position, boolean selected) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); Loading @@ -621,7 +629,7 @@ public class DirectoryFragment extends Fragment { @Override public void onSelectionChanged() { mSelectionManager.getSelection(mSelected); mModel.getSelection(mSelected); if (mSelected.size() > 0) { if (DEBUG) Log.d(TAG, "Maybe starting action mode."); if (mActionMode == null) { Loading Loading @@ -651,7 +659,7 @@ public class DirectoryFragment extends Fragment { if (DEBUG) Log.d(TAG, "Handling action mode destroyed."); mActionMode = null; // clear selection mSelectionManager.clearSelection(); mModel.clearSelection(); mSelected.clear(); mNoDeleteCount = 0; } Loading @@ -659,8 +667,8 @@ public class DirectoryFragment extends Fragment { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenuInflater().inflate(R.menu.mode_directory, menu); mode.setTitle(TextUtils.formatSelectedCount(mSelectionManager.getSelection().size())); return mSelectionManager.getSelection().size() > 0; mode.setTitle(TextUtils.formatSelectedCount(mModel.getSelection().size())); return mModel.getSelection().size() > 0; } @Override Loading @@ -679,8 +687,7 @@ public class DirectoryFragment extends Fragment { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { Selection selection = new Selection(); mSelectionManager.getSelection(selection); Selection selection = mModel.getSelection(new Selection()); final int id = item.getItemId(); if (id == R.id.menu_open) { Loading Loading @@ -948,50 +955,31 @@ public class DirectoryFragment extends Fragment { } } private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> implements DocumentContext { private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> { private final Context mContext; private final LayoutInflater mInflater; // TODO: Bring back support for footers. private final List<Footer> mFooters = new ArrayList<>(); private Cursor mCursor; private int mCursorCount; public DocumentsAdapter(Context context) { mContext = context; mInflater = LayoutInflater.from(context); } public void replaceResult(DirectoryResult result) { if (DEBUG) Log.i(TAG, "Updating adapter with new result set."); mCursor = result != null ? result.cursor : null; mCursorCount = mCursor != null ? mCursor.getCount() : 0; public void update() { mFooters.clear(); final Bundle extras = mCursor != null ? mCursor.getExtras() : null; if (extras != null) { final String info = extras.getString(DocumentsContract.EXTRA_INFO); if (info != null) { mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, info)); if (mModel.info != null) { mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, mModel.info)); } final String error = extras.getString(DocumentsContract.EXTRA_ERROR); if (error != null) { mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error)); if (mModel.error != null) { mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, mModel.error)); } if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) { if (mModel.isLoading()) { mFooters.add(new LoadingFooter()); } } if (result != null && result.exception != null) { mFooters.add(new MessageFooter( 3, R.drawable.ic_dialog_alert, getString(R.string.query_error))); } if (isEmpty()) { if (mModel.isEmpty()) { mEmptyView.setVisibility(View.VISIBLE); } else { mEmptyView.setVisibility(View.GONE); Loading Loading @@ -1025,7 +1013,7 @@ public class DirectoryFragment extends Fragment { final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache( context, mThumbSize); final Cursor cursor = getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); Loading @@ -1041,7 +1029,7 @@ public class DirectoryFragment extends Fragment { holder.docId = docId; final View itemView = holder.view; itemView.setActivated(mSelectionManager.getSelection().contains(position)); itemView.setActivated(mModel.isSelected(position)); final View line1 = itemView.findViewById(R.id.line1); final View line2 = itemView.findViewById(R.id.line2); Loading Loading @@ -1213,45 +1201,22 @@ public class DirectoryFragment extends Fragment { } } @Override public Cursor getCursor() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Can't call getCursor from non-main thread."); } return mCursor; } private Cursor getItem(int position) { if (position < mCursorCount) { mCursor.moveToPosition(position); return mCursor; } Log.w(TAG, "Returning null cursor for position: " + position); if (DEBUG) Log.d(TAG, "...Adapter size: " + mCursorCount); if (DEBUG) Log.d(TAG, "...Footer size: " + mFooters.size()); return null; } @Override public int getItemCount() { return mCursorCount; return mModel.getItemCount(); // return mCursorCount + mFooters.size(); } @Override public int getItemViewType(int position) { if (position < mCursorCount) { final int itemCount = mModel.getItemCount(); if (position < itemCount) { return 0; } else { position -= mCursorCount; position -= itemCount; return mFooters.get(position).getItemViewType(); } } private boolean isEmpty() { return getItemCount() > 0; } } private static String formatTime(Context context, long when) { Loading Loading @@ -1328,27 +1293,6 @@ public class DirectoryFragment extends Fragment { return MimePredicate.mimeMatches(state.acceptMimes, docMimeType); } private List<DocumentInfo> getSelectedDocuments() { Selection sel = mSelectionManager.getSelection(new Selection()); return getItemsAsDocuments(sel); } private List<DocumentInfo> getItemsAsDocuments(Selection items) { if (items == null || items.size() == 0) { return new ArrayList<>(0); } final List<DocumentInfo> docs = new ArrayList<>(items.size()); final int size = items.size(); for (int i = 0; i < size; i++) { final Cursor cursor = mAdapter.getItem(items.get(i)); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); docs.add(doc); } return docs; } private void copyFromClipboard() { new AsyncTask<Void, Void, List<DocumentInfo>>() { Loading Loading @@ -1426,7 +1370,7 @@ public class DirectoryFragment extends Fragment { } void copySelectedToClipboard() { Selection sel = mSelectionManager.getSelection(new Selection()); Selection sel = mModel.getSelection(new Selection()); copySelectionToClipboard(sel); } Loading Loading @@ -1475,7 +1419,7 @@ public class DirectoryFragment extends Fragment { } void selectAllFiles() { boolean changed = mSelectionManager.setItemsSelected(0, mAdapter.getItemCount(), true); boolean changed = mModel.selectAll(); if (changed) { updateDisplayState(); } Loading Loading @@ -1521,7 +1465,7 @@ public class DirectoryFragment extends Fragment { int dstPosition = mRecView.getChildAdapterPosition(v); DocumentInfo dstDir = null; if (dstPosition != android.widget.AdapterView.INVALID_POSITION) { Cursor dstCursor = mAdapter.getItem(dstPosition); Cursor dstCursor = mModel.getItem(dstPosition); checkNotNull(dstCursor, "Cursor cannot be null."); dstDir = DocumentInfo.fromDirectoryCursor(dstCursor); // TODO: Do not drop into the directory where the documents came from. Loading Loading @@ -1556,16 +1500,16 @@ public class DirectoryFragment extends Fragment { return Collections.EMPTY_LIST; } final List<DocumentInfo> selectedDocs = getSelectedDocuments(); final List<DocumentInfo> selectedDocs = mModel.getSelectedDocuments(); if (!selectedDocs.isEmpty()) { if (!mSelectionManager.getSelection().contains(position)) { if (!mModel.isSelected(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 Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); Loading Loading @@ -1671,12 +1615,14 @@ public class DirectoryFragment extends Fragment { mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension); } @Override public void onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint) { shadowSize.set(mShadowDimension, mShadowDimension); shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2); } @Override public void onDrawShadow(Canvas canvas) { mShadow.draw(canvas); } Loading Loading @@ -1706,7 +1652,7 @@ public class DirectoryFragment extends Fragment { extends AsyncTask<Selection, Void, List<DocumentInfo>> { @Override protected final List<DocumentInfo> doInBackground(Selection... selected) { return getItemsAsDocuments(selected[0]); return mModel.getDocuments(selected[0]); } @Override Loading Loading @@ -1775,4 +1721,136 @@ public class DirectoryFragment extends Fragment { @Override public void afterActivityCreated(DirectoryFragment fragment) {} } /** * The data model for the current loaded directory. */ private final class Model implements DocumentContext { private MultiSelectManager mSelectionManager; private int mCursorCount; private boolean mIsLoading; @Nullable private Cursor mCursor; @Nullable private String info; @Nullable private String error; /** * Sets the selection manager used by the model. * TODO: the model should instantiate the selection manager. See onActivityCreated. */ void setSelectionManager(MultiSelectManager mgr) { mSelectionManager = mgr; } /** * Selects all files in the current directory. * @return true if the selection state changed for any files. */ boolean selectAll() { return mSelectionManager.setItemsSelected(0, mCursorCount, true); } /** * Clones the current selection into the given Selection object. * @param selection * @return The selection that was passed in, for convenience. */ Selection getSelection(Selection selection) { return mSelectionManager.getSelection(selection); } /** * @return The current selection (the live instance, not a copy). */ Selection getSelection() { return mSelectionManager.getSelection(); } boolean isSelected(int position) { return mSelectionManager.getSelection().contains(position); } void clearSelection() { mSelectionManager.clearSelection(); } void update(DirectoryResult result) { if (DEBUG) Log.i(TAG, "Updating model with new result set."); if (result == null) { mCursor = null; mCursorCount = 0; info = null; error = null; mIsLoading = false; return; } if (result.exception != null) { Log.e(TAG, "Error while loading directory contents", result.exception); error = getString(R.string.query_error); return; } mCursor = result.cursor; mCursorCount = mCursor.getCount(); final Bundle extras = mCursor.getExtras(); if (extras != null) { info = extras.getString(DocumentsContract.EXTRA_INFO); error = extras.getString(DocumentsContract.EXTRA_ERROR); mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false); } } private int getItemCount() { return mCursorCount; } private Cursor getItem(int position) { if (position >= mCursorCount) { throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " + mCursorCount + " items"); } mCursor.moveToPosition(position); return mCursor; } private boolean isEmpty() { return mCursorCount == 0; } private boolean isLoading() { return mIsLoading; } private List<DocumentInfo> getSelectedDocuments() { Selection sel = getSelection(new Selection()); return getDocuments(sel); } private List<DocumentInfo> getDocuments(Selection items) { if (items == null || items.size() == 0) { return new ArrayList<>(0); } final List<DocumentInfo> docs = new ArrayList<>(items.size()); final int size = items.size(); for (int i = 0; i < size; i++) { final Cursor cursor = getItem(items.get(i)); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); docs.add(doc); } return docs; } @Override public Cursor getCursor() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Can't call getCursor from non-main thread."); } return mCursor; } } } packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java +3 −3 Original line number Diff line number Diff line Loading @@ -184,13 +184,13 @@ public final class MultiSelectManager { /** * Returns a Selection object that provides a live view * on the current selection. Callers wishing to get * on the current selection. * * @see #getSelectionSnapshot() on how to get a snapshot * @see #getSelection(Selection) on how to get a snapshot * of the selection that will not reflect future changes * to selection. * * @return The current seleciton. * @return The current selection. */ public Selection getSelection() { return mSelection; Loading Loading
packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +185 −107 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.os.Parcelable; import android.os.SystemProperties; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.support.annotation.Nullable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; Loading Loading @@ -96,7 +97,6 @@ import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; import com.google.common.collect.Lists; import java.util.ArrayList; Loading Loading @@ -129,6 +129,8 @@ public class DirectoryFragment extends Fragment { private static final String EXTRA_QUERY = "query"; private static final String EXTRA_IGNORE_STATE = "ignoreState"; private final Model mModel = new Model(); private final Handler mHandler = new Handler(Looper.getMainLooper()); private View mEmptyView; Loading @@ -147,7 +149,6 @@ public class DirectoryFragment extends Fragment { private LoaderCallbacks<DirectoryResult> mCallbacks; private FragmentTuner mFragmentTuner; private DocumentClipper mClipper; private MultiSelectManager mSelectionManager; // These are lazily initialized. private LinearLayoutManager mListLayout; private GridLayoutManager mGridLayout; Loading Loading @@ -261,7 +262,7 @@ public class DirectoryFragment extends Fragment { } // Clear any outstanding selection mSelectionManager.clearSelection(); mModel.clearSelection(); } @Override Loading Loading @@ -290,14 +291,17 @@ public class DirectoryFragment extends Fragment { } }; mSelectionManager = new MultiSelectManager( // TODO: instead of inserting the view into the constructor, extract listener-creation code // and set the listener on the view after the fact. Then the view doesn't need to be passed // into the selection manager which is passed into the model. MultiSelectManager selMgr= new MultiSelectManager( mRecView, listener, state.allowMultiple ? MultiSelectManager.MODE_MULTIPLE : MultiSelectManager.MODE_SINGLE); mSelectionManager.addCallback(new SelectionModeListener()); selMgr.addCallback(new SelectionModeListener()); mModel.setSelectionManager(selMgr); mType = getArguments().getInt(EXTRA_TYPE); mStateKey = buildStateKey(root, doc); Loading Loading @@ -367,7 +371,9 @@ public class DirectoryFragment extends Fragment { if (!isAdded()) return; mAdapter.replaceResult(result); // TODO: make the adapter listen to the model mModel.update(result); mAdapter.update(); // Push latest state up to UI // TODO: if mode change was racing with us, don't overwrite it Loading @@ -380,7 +386,7 @@ public class DirectoryFragment extends Fragment { updateDisplayState(); // When launched into empty recents, show drawer if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched && if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched && context instanceof DocumentsActivity) { ((DocumentsActivity) context).setRootsDrawerOpen(true); } Loading @@ -398,7 +404,9 @@ public class DirectoryFragment extends Fragment { @Override public void onLoaderReset(Loader<DirectoryResult> loader) { mAdapter.replaceResult(null); // TODO: make the adapter listen to the model. mModel.update(null); mAdapter.update(); } }; Loading Loading @@ -433,7 +441,7 @@ public class DirectoryFragment extends Fragment { } private boolean onSingleTapUp(MotionEvent e) { if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) { if (Events.isTouchEvent(e) && mModel.getSelection().isEmpty()) { int position = getEventAdapterPosition(e); if (position != RecyclerView.NO_POSITION) { return handleViewItem(position); Loading @@ -454,14 +462,14 @@ public class DirectoryFragment extends Fragment { } private boolean handleViewItem(int position) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (isDocumentEnabled(docMimeType, docFlags)) { final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); ((BaseActivity) getActivity()).onDocumentPicked(doc, mAdapter); mSelectionManager.clearSelection(); ((BaseActivity) getActivity()).onDocumentPicked(doc, mModel); mModel.clearSelection(); return true; } return false; Loading Loading @@ -598,7 +606,7 @@ public class DirectoryFragment extends Fragment { public boolean onBeforeItemStateChange(int position, boolean selected) { // Directories and footer items cannot be checked if (selected) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); Loading @@ -610,7 +618,7 @@ public class DirectoryFragment extends Fragment { @Override public void onItemStateChanged(int position, boolean selected) { final Cursor cursor = mAdapter.getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); Loading @@ -621,7 +629,7 @@ public class DirectoryFragment extends Fragment { @Override public void onSelectionChanged() { mSelectionManager.getSelection(mSelected); mModel.getSelection(mSelected); if (mSelected.size() > 0) { if (DEBUG) Log.d(TAG, "Maybe starting action mode."); if (mActionMode == null) { Loading Loading @@ -651,7 +659,7 @@ public class DirectoryFragment extends Fragment { if (DEBUG) Log.d(TAG, "Handling action mode destroyed."); mActionMode = null; // clear selection mSelectionManager.clearSelection(); mModel.clearSelection(); mSelected.clear(); mNoDeleteCount = 0; } Loading @@ -659,8 +667,8 @@ public class DirectoryFragment extends Fragment { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenuInflater().inflate(R.menu.mode_directory, menu); mode.setTitle(TextUtils.formatSelectedCount(mSelectionManager.getSelection().size())); return mSelectionManager.getSelection().size() > 0; mode.setTitle(TextUtils.formatSelectedCount(mModel.getSelection().size())); return mModel.getSelection().size() > 0; } @Override Loading @@ -679,8 +687,7 @@ public class DirectoryFragment extends Fragment { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { Selection selection = new Selection(); mSelectionManager.getSelection(selection); Selection selection = mModel.getSelection(new Selection()); final int id = item.getItemId(); if (id == R.id.menu_open) { Loading Loading @@ -948,50 +955,31 @@ public class DirectoryFragment extends Fragment { } } private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> implements DocumentContext { private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> { private final Context mContext; private final LayoutInflater mInflater; // TODO: Bring back support for footers. private final List<Footer> mFooters = new ArrayList<>(); private Cursor mCursor; private int mCursorCount; public DocumentsAdapter(Context context) { mContext = context; mInflater = LayoutInflater.from(context); } public void replaceResult(DirectoryResult result) { if (DEBUG) Log.i(TAG, "Updating adapter with new result set."); mCursor = result != null ? result.cursor : null; mCursorCount = mCursor != null ? mCursor.getCount() : 0; public void update() { mFooters.clear(); final Bundle extras = mCursor != null ? mCursor.getExtras() : null; if (extras != null) { final String info = extras.getString(DocumentsContract.EXTRA_INFO); if (info != null) { mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, info)); if (mModel.info != null) { mFooters.add(new MessageFooter(2, R.drawable.ic_dialog_info, mModel.info)); } final String error = extras.getString(DocumentsContract.EXTRA_ERROR); if (error != null) { mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, error)); if (mModel.error != null) { mFooters.add(new MessageFooter(3, R.drawable.ic_dialog_alert, mModel.error)); } if (extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)) { if (mModel.isLoading()) { mFooters.add(new LoadingFooter()); } } if (result != null && result.exception != null) { mFooters.add(new MessageFooter( 3, R.drawable.ic_dialog_alert, getString(R.string.query_error))); } if (isEmpty()) { if (mModel.isEmpty()) { mEmptyView.setVisibility(View.VISIBLE); } else { mEmptyView.setVisibility(View.GONE); Loading Loading @@ -1025,7 +1013,7 @@ public class DirectoryFragment extends Fragment { final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache( context, mThumbSize); final Cursor cursor = getItem(position); final Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); Loading @@ -1041,7 +1029,7 @@ public class DirectoryFragment extends Fragment { holder.docId = docId; final View itemView = holder.view; itemView.setActivated(mSelectionManager.getSelection().contains(position)); itemView.setActivated(mModel.isSelected(position)); final View line1 = itemView.findViewById(R.id.line1); final View line2 = itemView.findViewById(R.id.line2); Loading Loading @@ -1213,45 +1201,22 @@ public class DirectoryFragment extends Fragment { } } @Override public Cursor getCursor() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Can't call getCursor from non-main thread."); } return mCursor; } private Cursor getItem(int position) { if (position < mCursorCount) { mCursor.moveToPosition(position); return mCursor; } Log.w(TAG, "Returning null cursor for position: " + position); if (DEBUG) Log.d(TAG, "...Adapter size: " + mCursorCount); if (DEBUG) Log.d(TAG, "...Footer size: " + mFooters.size()); return null; } @Override public int getItemCount() { return mCursorCount; return mModel.getItemCount(); // return mCursorCount + mFooters.size(); } @Override public int getItemViewType(int position) { if (position < mCursorCount) { final int itemCount = mModel.getItemCount(); if (position < itemCount) { return 0; } else { position -= mCursorCount; position -= itemCount; return mFooters.get(position).getItemViewType(); } } private boolean isEmpty() { return getItemCount() > 0; } } private static String formatTime(Context context, long when) { Loading Loading @@ -1328,27 +1293,6 @@ public class DirectoryFragment extends Fragment { return MimePredicate.mimeMatches(state.acceptMimes, docMimeType); } private List<DocumentInfo> getSelectedDocuments() { Selection sel = mSelectionManager.getSelection(new Selection()); return getItemsAsDocuments(sel); } private List<DocumentInfo> getItemsAsDocuments(Selection items) { if (items == null || items.size() == 0) { return new ArrayList<>(0); } final List<DocumentInfo> docs = new ArrayList<>(items.size()); final int size = items.size(); for (int i = 0; i < size; i++) { final Cursor cursor = mAdapter.getItem(items.get(i)); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); docs.add(doc); } return docs; } private void copyFromClipboard() { new AsyncTask<Void, Void, List<DocumentInfo>>() { Loading Loading @@ -1426,7 +1370,7 @@ public class DirectoryFragment extends Fragment { } void copySelectedToClipboard() { Selection sel = mSelectionManager.getSelection(new Selection()); Selection sel = mModel.getSelection(new Selection()); copySelectionToClipboard(sel); } Loading Loading @@ -1475,7 +1419,7 @@ public class DirectoryFragment extends Fragment { } void selectAllFiles() { boolean changed = mSelectionManager.setItemsSelected(0, mAdapter.getItemCount(), true); boolean changed = mModel.selectAll(); if (changed) { updateDisplayState(); } Loading Loading @@ -1521,7 +1465,7 @@ public class DirectoryFragment extends Fragment { int dstPosition = mRecView.getChildAdapterPosition(v); DocumentInfo dstDir = null; if (dstPosition != android.widget.AdapterView.INVALID_POSITION) { Cursor dstCursor = mAdapter.getItem(dstPosition); Cursor dstCursor = mModel.getItem(dstPosition); checkNotNull(dstCursor, "Cursor cannot be null."); dstDir = DocumentInfo.fromDirectoryCursor(dstCursor); // TODO: Do not drop into the directory where the documents came from. Loading Loading @@ -1556,16 +1500,16 @@ public class DirectoryFragment extends Fragment { return Collections.EMPTY_LIST; } final List<DocumentInfo> selectedDocs = getSelectedDocuments(); final List<DocumentInfo> selectedDocs = mModel.getSelectedDocuments(); if (!selectedDocs.isEmpty()) { if (!mSelectionManager.getSelection().contains(position)) { if (!mModel.isSelected(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 Cursor cursor = mModel.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); Loading Loading @@ -1671,12 +1615,14 @@ public class DirectoryFragment extends Fragment { mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension); } @Override public void onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint) { shadowSize.set(mShadowDimension, mShadowDimension); shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2); } @Override public void onDrawShadow(Canvas canvas) { mShadow.draw(canvas); } Loading Loading @@ -1706,7 +1652,7 @@ public class DirectoryFragment extends Fragment { extends AsyncTask<Selection, Void, List<DocumentInfo>> { @Override protected final List<DocumentInfo> doInBackground(Selection... selected) { return getItemsAsDocuments(selected[0]); return mModel.getDocuments(selected[0]); } @Override Loading Loading @@ -1775,4 +1721,136 @@ public class DirectoryFragment extends Fragment { @Override public void afterActivityCreated(DirectoryFragment fragment) {} } /** * The data model for the current loaded directory. */ private final class Model implements DocumentContext { private MultiSelectManager mSelectionManager; private int mCursorCount; private boolean mIsLoading; @Nullable private Cursor mCursor; @Nullable private String info; @Nullable private String error; /** * Sets the selection manager used by the model. * TODO: the model should instantiate the selection manager. See onActivityCreated. */ void setSelectionManager(MultiSelectManager mgr) { mSelectionManager = mgr; } /** * Selects all files in the current directory. * @return true if the selection state changed for any files. */ boolean selectAll() { return mSelectionManager.setItemsSelected(0, mCursorCount, true); } /** * Clones the current selection into the given Selection object. * @param selection * @return The selection that was passed in, for convenience. */ Selection getSelection(Selection selection) { return mSelectionManager.getSelection(selection); } /** * @return The current selection (the live instance, not a copy). */ Selection getSelection() { return mSelectionManager.getSelection(); } boolean isSelected(int position) { return mSelectionManager.getSelection().contains(position); } void clearSelection() { mSelectionManager.clearSelection(); } void update(DirectoryResult result) { if (DEBUG) Log.i(TAG, "Updating model with new result set."); if (result == null) { mCursor = null; mCursorCount = 0; info = null; error = null; mIsLoading = false; return; } if (result.exception != null) { Log.e(TAG, "Error while loading directory contents", result.exception); error = getString(R.string.query_error); return; } mCursor = result.cursor; mCursorCount = mCursor.getCount(); final Bundle extras = mCursor.getExtras(); if (extras != null) { info = extras.getString(DocumentsContract.EXTRA_INFO); error = extras.getString(DocumentsContract.EXTRA_ERROR); mIsLoading = extras.getBoolean(DocumentsContract.EXTRA_LOADING, false); } } private int getItemCount() { return mCursorCount; } private Cursor getItem(int position) { if (position >= mCursorCount) { throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " + mCursorCount + " items"); } mCursor.moveToPosition(position); return mCursor; } private boolean isEmpty() { return mCursorCount == 0; } private boolean isLoading() { return mIsLoading; } private List<DocumentInfo> getSelectedDocuments() { Selection sel = getSelection(new Selection()); return getDocuments(sel); } private List<DocumentInfo> getDocuments(Selection items) { if (items == null || items.size() == 0) { return new ArrayList<>(0); } final List<DocumentInfo> docs = new ArrayList<>(items.size()); final int size = items.size(); for (int i = 0; i < size; i++) { final Cursor cursor = getItem(items.get(i)); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); docs.add(doc); } return docs; } @Override public Cursor getCursor() { if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("Can't call getCursor from non-main thread."); } return mCursor; } } }
packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java +3 −3 Original line number Diff line number Diff line Loading @@ -184,13 +184,13 @@ public final class MultiSelectManager { /** * Returns a Selection object that provides a live view * on the current selection. Callers wishing to get * on the current selection. * * @see #getSelectionSnapshot() on how to get a snapshot * @see #getSelection(Selection) on how to get a snapshot * of the selection that will not reflect future changes * to selection. * * @return The current seleciton. * @return The current selection. */ public Selection getSelection() { return mSelection; Loading