Merge "Support sorting in storage UI."
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 69ee466..ae11a8c 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -12,11 +12,21 @@
<intent-filter android:priority="100">
<action android:name="android.intent.action.OPEN_DOCUMENT" />
<category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
</intent-filter>
<intent-filter android:priority="100">
<action android:name="android.intent.action.CREATE_DOCUMENT" />
- <data android:mimeType="*/*"/>
<category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ </activity>
+
+ <!-- TODO: remove when we have real clients -->
+ <activity android:name=".TestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
diff --git a/packages/DocumentsUI/res/menu/directory.xml b/packages/DocumentsUI/res/menu/directory.xml
index 52c8f5c..c1fa228 100644
--- a/packages/DocumentsUI/res/menu/directory.xml
+++ b/packages/DocumentsUI/res/menu/directory.xml
@@ -18,13 +18,16 @@
<item
android:id="@+id/menu_grid"
android:title="@string/menu_grid"
- android:icon="@drawable/ic_menu_grid" />
+ android:icon="@drawable/ic_menu_grid"
+ android:showAsAction="ifRoom" />
<item
android:id="@+id/menu_list"
android:title="@string/menu_list"
- android:icon="@drawable/ic_menu_list" />
+ android:icon="@drawable/ic_menu_list"
+ android:showAsAction="ifRoom" />
<item
android:id="@+id/menu_sort"
android:title="@string/menu_sort"
- android:icon="@drawable/ic_menu_sort" />
+ android:icon="@drawable/ic_menu_sort"
+ android:showAsAction="ifRoom" />
</menu>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 141ba80..6ae2d12 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -30,4 +30,7 @@
<string name="mode_selected_count"><xliff:g id="count" example="3">%1$d</xliff:g> selected</string>
+ <string name="sort_name">Name</string>
+ <string name="sort_date">Date modified</string>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index bae42d5..531eaf3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -16,12 +16,17 @@
package com.android.documentsui;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
@@ -47,6 +52,7 @@
import android.widget.ListView;
import android.widget.TextView;
+import com.android.documentsui.DocumentsActivity.DisplayState;
import com.android.documentsui.DocumentsActivity.Document;
import com.google.android.collect.Lists;
@@ -58,7 +64,8 @@
public class DirectoryFragment extends Fragment {
// TODO: show storage backend in item views when requested
- // TODO: implement sorting dialog
+
+ private static final String TAG_SORT = "sort";
private ListView mListView;
private GridView mGridView;
@@ -71,20 +78,12 @@
private int mFlags;
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, boolean allowMultiple) {
+ public static void show(FragmentManager fm, Uri uri, String displayName) {
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);
@@ -127,8 +126,18 @@
mCallbacks = new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ final DisplayState state = getDisplayState(DirectoryFragment.this);
+ final String sortOrder;
+ if (state.sortBy == DisplayState.SORT_BY_NAME) {
+ sortOrder = DocumentColumns.DISPLAY_NAME + " ASC";
+ } else if (state.sortBy == DisplayState.SORT_BY_DATE) {
+ sortOrder = DocumentColumns.LAST_MODIFIED + " DESC";
+ } else {
+ sortOrder = null;
+ }
+
final Uri contentsUri = DocumentsContract.buildContentsUri(uri);
- return new CursorLoader(context, contentsUri, null, null, null, null);
+ return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
}
@Override
@@ -170,21 +179,27 @@
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- final int mode = getMode();
- menu.findItem(R.id.menu_grid).setVisible(mode != MODE_GRID);
- menu.findItem(R.id.menu_list).setVisible(mode != MODE_LIST);
+ final DisplayState state = getDisplayState(this);
+ menu.findItem(R.id.menu_grid).setVisible(state.mode != DisplayState.MODE_GRID);
+ menu.findItem(R.id.menu_list).setVisible(state.mode != DisplayState.MODE_LIST);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ final DisplayState state = getDisplayState(this);
final int id = item.getItemId();
if (id == R.id.menu_grid) {
- getArguments().putInt(EXTRA_MODE, MODE_GRID);
+ state.mode = DisplayState.MODE_GRID;
updateMode();
+ getFragmentManager().invalidateOptionsMenu();
return true;
} else if (id == R.id.menu_list) {
- getArguments().putInt(EXTRA_MODE, MODE_LIST);
+ state.mode = DisplayState.MODE_LIST;
updateMode();
+ getFragmentManager().invalidateOptionsMenu();
+ return true;
+ } else if (id == R.id.menu_sort) {
+ SortFragment.show(this);
return true;
} else {
return super.onOptionsItemSelected(item);
@@ -192,19 +207,19 @@
}
private void updateMode() {
- final int mode = getMode();
+ final DisplayState state = getDisplayState(this);
- mListView.setVisibility(mode == MODE_LIST ? View.VISIBLE : View.GONE);
- mGridView.setVisibility(mode == MODE_GRID ? View.VISIBLE : View.GONE);
+ mListView.setVisibility(state.mode == DisplayState.MODE_LIST ? View.VISIBLE : View.GONE);
+ mGridView.setVisibility(state.mode == DisplayState.MODE_GRID ? View.VISIBLE : View.GONE);
final int choiceMode;
- if (getArguments().getBoolean(EXTRA_ALLOW_MULTIPLE)) {
+ if (state.allowMultiple) {
choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL;
} else {
choiceMode = ListView.CHOICE_MODE_NONE;
}
- if (mode == MODE_GRID) {
+ if (state.mode == DisplayState.MODE_GRID) {
mListView.setAdapter(null);
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mGridView.setAdapter(mAdapter);
@@ -212,15 +227,21 @@
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setChoiceMode(choiceMode);
mCurrentView = mGridView;
- } else {
+ } else if (state.mode == DisplayState.MODE_LIST) {
mGridView.setAdapter(null);
mGridView.setChoiceMode(ListView.CHOICE_MODE_NONE);
mListView.setAdapter(mAdapter);
mListView.setChoiceMode(choiceMode);
mCurrentView = mListView;
+ } else {
+ throw new IllegalStateException();
}
}
+ private void updateSortBy() {
+ getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
+ }
+
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -288,12 +309,8 @@
}
};
- private boolean getSupportsCreate() {
- return (mFlags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
- }
-
- private int getMode() {
- return getArguments().getInt(EXTRA_MODE, MODE_LIST);
+ private static DisplayState getDisplayState(Fragment fragment) {
+ return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
}
private class DocumentsAdapter extends CursorAdapter {
@@ -304,10 +321,10 @@
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
- final int mode = getMode();
- if (mode == MODE_LIST) {
+ final DisplayState state = getDisplayState(DirectoryFragment.this);
+ if (state.mode == DisplayState.MODE_LIST) {
return inflater.inflate(R.layout.item_doc_list, parent, false);
- } else if (mode == MODE_GRID) {
+ } else if (state.mode == DisplayState.MODE_GRID) {
return inflater.inflate(R.layout.item_doc_grid, parent, false);
} else {
throw new IllegalStateException();
@@ -341,6 +358,38 @@
}
}
+ public static class SortFragment extends DialogFragment {
+ public static void show(DirectoryFragment parent) {
+ if (!parent.isAdded()) return;
+
+ final SortFragment dialog = new SortFragment();
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_SORT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final DisplayState state = getDisplayState(this);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.menu_sort);
+ builder.setSingleChoiceItems(new CharSequence[] {
+ getText(R.string.sort_name),
+ getText(R.string.sort_date),
+ }, state.sortBy, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ state.sortBy = which;
+ ((DirectoryFragment) getTargetFragment()).updateSortBy();
+ dismiss();
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
private static int getDocumentFlags(Context context, Uri uri) {
final Cursor cursor = context.getContentResolver().query(uri, new String[] {
DocumentColumns.FLAGS }, null, null, null);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ca4ea82..c45d2b4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -55,13 +55,14 @@
// TODO: fragment to show recently opened documents
// TODO: pull actionbar icon from current backend
- private static final int MODE_OPEN = 1;
- private static final int MODE_CREATE = 2;
+ private static final int ACTION_OPEN = 1;
+ private static final int ACTION_CREATE = 2;
- private int mMode;
- private boolean mAllowMultiple;
+ private int mAction;
private String[] mAcceptMimes;
+ private final DisplayState mDisplayState = new DisplayState();
+
private boolean mIgnoreNextNavigation;
private Uri mCurrentDir;
@@ -74,11 +75,11 @@
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
- mMode = MODE_OPEN;
- mAllowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
+ mAction = ACTION_OPEN;
+ mDisplayState.allowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
- mMode = MODE_CREATE;
- mAllowMultiple = false;
+ mAction = ACTION_CREATE;
+ mDisplayState.allowMultiple = false;
}
if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
@@ -87,6 +88,12 @@
mAcceptMimes = new String[] { intent.getType() };
}
+ if (mimeMatches("image/*", mAcceptMimes)) {
+ mDisplayState.mode = DisplayState.MODE_GRID;
+ } else {
+ mDisplayState.mode = DisplayState.MODE_LIST;
+ }
+
setResult(Activity.RESULT_CANCELED);
setContentView(R.layout.activity);
@@ -95,7 +102,7 @@
updateActionBar();
- if (mMode == MODE_CREATE) {
+ if (mAction == ACTION_CREATE) {
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
@@ -120,9 +127,9 @@
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(false);
- if (mMode == MODE_OPEN) {
+ if (mAction == ACTION_OPEN) {
actionBar.setTitle(R.string.title_open);
- } else if (mMode == MODE_CREATE) {
+ } else if (mAction == ACTION_CREATE) {
actionBar.setTitle(R.string.title_save);
}
}
@@ -140,7 +147,7 @@
super.onPrepareOptionsMenu(menu);
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- createDir.setVisible(mMode == MODE_CREATE);
+ createDir.setVisible(mAction == ACTION_CREATE);
createDir.setEnabled(mCurrentSupportsCreate);
return true;
@@ -209,11 +216,15 @@
}
};
+ public DisplayState getDisplayState() {
+ return mDisplayState;
+ }
+
public void onDirectoryChanged(Uri uri, int flags) {
mCurrentDir = uri;
mCurrentSupportsCreate = (flags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
- if (mMode == MODE_CREATE) {
+ if (mAction == ACTION_CREATE) {
final FragmentManager fm = getFragmentManager();
SaveFragment.get(fm).setSaveEnabled(mCurrentSupportsCreate);
}
@@ -225,18 +236,18 @@
final Uri uri = DocumentsContract.buildDocumentUri(
info.authority, DocumentsContract.ROOT_GUID);
final CharSequence displayName = info.loadLabel(getPackageManager());
- DirectoryFragment.show(getFragmentManager(), uri, displayName.toString(), mAllowMultiple);
+ DirectoryFragment.show(getFragmentManager(), uri, displayName.toString());
}
public void onDocumentPicked(Document doc) {
final FragmentManager fm = getFragmentManager();
if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(doc.mimeType)) {
// Nested directory picked, recurse using new fragment
- DirectoryFragment.show(fm, doc.uri, doc.displayName, mAllowMultiple);
- } else if (mMode == MODE_OPEN) {
+ DirectoryFragment.show(fm, doc.uri, doc.displayName);
+ } else if (mAction == ACTION_OPEN) {
// Explicit file picked, return
onFinished(doc.uri);
- } else if (mMode == MODE_CREATE) {
+ } else if (mAction == ACTION_CREATE) {
// Overwrite current filename
SaveFragment.get(fm).setDisplayName(doc.displayName);
}
@@ -274,7 +285,7 @@
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
- if (mMode == MODE_CREATE) {
+ if (mAction == ACTION_CREATE) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
@@ -282,6 +293,18 @@
finish();
}
+ public static class DisplayState {
+ public int mode;
+ public int sortBy;
+ public boolean allowMultiple;
+
+ public static final int MODE_LIST = 0;
+ public static final int MODE_GRID = 1;
+
+ public static final int SORT_BY_NAME = 0;
+ public static final int SORT_BY_DATE = 1;
+ }
+
public static class Document {
public Uri uri;
public String mimeType;
@@ -297,6 +320,27 @@
}
}
+ public static boolean mimeMatches(String filter, String[] tests) {
+ for (String test : tests) {
+ if (mimeMatches(filter, test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean mimeMatches(String filter, String test) {
+ if (filter.equals(test)) {
+ return true;
+ } else if ("*/*".equals(filter)) {
+ return true;
+ } else if (filter.endsWith("/*")) {
+ return filter.regionMatches(0, test, 0, filter.indexOf('/'));
+ } else {
+ return false;
+ }
+ }
+
public static Drawable resolveDocumentIcon(Context context, String mimeType) {
// TODO: allow backends to provide custom MIME icons
if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
new file mode 100644
index 0000000..b15d123
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 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;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class TestActivity extends Activity {
+ private TextView mResult;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Context context = this;
+
+ final LinearLayout view = new LinearLayout(context);
+ view.setOrientation(LinearLayout.VERTICAL);
+
+ final CheckBox multiple = new CheckBox(context);
+ multiple.setText("ALLOW_MULTIPLE");
+ view.addView(multiple);
+
+ Button button;
+ button = new Button(context);
+ button.setText("OPEN_DOC */*");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("OPEN_DOC image/*");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("image/*");
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("OPEN_DOC text/plain, application/msword");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
+ "text/plain", "application/msword" });
+ if (multiple.isChecked()) {
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
+ }
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ button = new Button(context);
+ button.setText("CREATE_DOC text/plain");
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt");
+ startActivityForResult(intent, 42);
+ }
+ });
+ view.addView(button);
+
+ mResult = new TextView(context);
+ view.addView(mResult);
+
+ setContentView(view);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mResult.setText("resultCode=" + resultCode + ", data=" + String.valueOf(data));
+ }
+}