diff options
6 files changed, 279 insertions, 53 deletions
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.ImageView; 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 @@ import java.util.ArrayList; 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { @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 @@ public class DirectoryFragment extends Fragment { } 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { } }; - 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 @@ public class DirectoryFragment extends Fragment { @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 class DirectoryFragment extends Fragment { } } + 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 @@ public class DocumentsActivity extends Activity { // 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 class DocumentsActivity extends Activity { } }; + 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 @@ public class DocumentsActivity extends Activity { 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 class DocumentsActivity extends Activity { } } + 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)); + } +} |