Support multi-select in storage UI.

When caller has specified that multiple documents are okay, enable
multi-select action mode.  Currently only allows document selection,
not directories.  Returns multiple documents through ClipData.

Fix bug where GridView was stuck with 2 columns on tablets.

Change-Id: Id49b29a86330639b56fa116d37e7f0d874980c5b
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index 5ba1930..bae42d5 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -29,12 +29,16 @@
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.DocumentColumns;
 import android.text.format.DateUtils;
+import android.util.SparseBooleanArray;
+import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.MultiChoiceModeListener;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.CursorAdapter;
@@ -44,6 +48,9 @@
 import android.widget.TextView;
 
 import com.android.documentsui.DocumentsActivity.Document;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
 
 /**
  * Display the documents inside a single directory.
@@ -52,11 +59,12 @@
 
     // TODO: show storage backend in item views when requested
     // TODO: implement sorting dialog
-    // TODO: support multiple selection with actionmode
 
     private ListView mListView;
     private GridView mGridView;
 
+    private AbsListView mCurrentView;
+
     private DocumentsAdapter mAdapter;
     private LoaderCallbacks<Cursor> mCallbacks;
 
@@ -64,16 +72,19 @@
 
     private static final String EXTRA_URI = "uri";
     private static final String EXTRA_MODE = "display_mode";
+    private static final String EXTRA_ALLOW_MULTIPLE = "allow_multiple";
 
     private static final int MODE_LIST = 1;
     private static final int MODE_GRID = 2;
 
     private static final int LOADER_DOCUMENTS = 2;
 
-    public static void show(FragmentManager fm, Uri uri, String displayName) {
+    public static void show(
+            FragmentManager fm, Uri uri, String displayName, boolean allowMultiple) {
         final Bundle args = new Bundle();
         args.putParcelable(EXTRA_URI, uri);
         args.putInt(EXTRA_MODE, MODE_LIST);
+        args.putBoolean(EXTRA_ALLOW_MULTIPLE, allowMultiple);
 
         final DirectoryFragment fragment = new DirectoryFragment();
         fragment.setArguments(args);
@@ -100,9 +111,11 @@
 
         mListView = (ListView) view.findViewById(R.id.list);
         mListView.setOnItemClickListener(mItemListener);
+        mListView.setMultiChoiceModeListener(mMultiListener);
 
         mGridView = (GridView) view.findViewById(R.id.grid);
         mGridView.setOnItemClickListener(mItemListener);
+        mGridView.setMultiChoiceModeListener(mMultiListener);
 
         mAdapter = new DocumentsAdapter(context);
         updateMode();
@@ -184,13 +197,27 @@
         mListView.setVisibility(mode == MODE_LIST ? View.VISIBLE : View.GONE);
         mGridView.setVisibility(mode == MODE_GRID ? View.VISIBLE : View.GONE);
 
+        final int choiceMode;
+        if (getArguments().getBoolean(EXTRA_ALLOW_MULTIPLE)) {
+            choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
+        } else {
+            choiceMode = ListView.CHOICE_MODE_NONE;
+        }
+
         if (mode == MODE_GRID) {
             mListView.setAdapter(null);
+            mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
             mGridView.setAdapter(mAdapter);
+            mGridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.grid_width));
             mGridView.setNumColumns(GridView.AUTO_FIT);
+            mGridView.setChoiceMode(choiceMode);
+            mCurrentView = mGridView;
         } else {
             mGridView.setAdapter(null);
+            mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
             mListView.setAdapter(mAdapter);
+            mListView.setChoiceMode(choiceMode);
+            mCurrentView = mListView;
         }
     }
 
@@ -198,13 +225,69 @@
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final Cursor cursor = (Cursor) mAdapter.getItem(position);
-
             final Uri uri = getArguments().getParcelable(EXTRA_URI);
             final Document doc = Document.fromCursor(uri.getAuthority(), cursor);
             ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
         }
     };
 
+    private MultiChoiceModeListener mMultiListener = new MultiChoiceModeListener() {
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+            return true;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            if (item.getItemId() == R.id.menu_open) {
+                final Uri uri = getArguments().getParcelable(EXTRA_URI);
+                final SparseBooleanArray checked = mCurrentView.getCheckedItemPositions();
+                final ArrayList<Document> docs = Lists.newArrayList();
+
+                final int size = checked.size();
+                for (int i = 0; i < size; i++) {
+                    if (checked.valueAt(i)) {
+                        final Cursor cursor = (Cursor) mAdapter.getItem(checked.keyAt(i));
+                        docs.add(Document.fromCursor(uri.getAuthority(), cursor));
+                    }
+                }
+
+                ((DocumentsActivity) getActivity()).onDocumentsPicked(docs);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            // ignored
+        }
+
+        @Override
+        public void onItemCheckedStateChanged(
+                ActionMode mode, int position, long id, boolean checked) {
+            if (checked) {
+                final Cursor cursor = (Cursor) mAdapter.getItem(position);
+                final String mimeType = getCursorString(cursor, DocumentColumns.MIME_TYPE);
+
+                // Directories cannot be checked
+                if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
+                    mCurrentView.setItemChecked(position, false);
+                }
+            }
+
+            mode.setTitle(getResources()
+                    .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
+        }
+    };
+
     private boolean getSupportsCreate() {
         return (mFlags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
     }