Sliding drawer for roots, move sorting, search.
Move backend root exploration into sliding drawer, and adjust action bar when moving between modes. Moves sorting into action bar spinner instead of separate dialog. Also add initial search support. Change-Id: I70189911ba56ae6bd93d5c503a8600acd6d6c0c5
This commit is contained in:
@@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
|
||||
|
||||
LOCAL_PACKAGE_NAME := DocumentsUI
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
|
||||
BIN
packages/DocumentsUI/res/drawable-hdpi/drawer_shadow.9.png
Normal file
BIN
packages/DocumentsUI/res/drawable-hdpi/drawer_shadow.9.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 B |
BIN
packages/DocumentsUI/res/drawable-hdpi/ic_drawer.png
Normal file
BIN
packages/DocumentsUI/res/drawable-hdpi/ic_drawer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
packages/DocumentsUI/res/drawable-hdpi/ic_menu_search.png
Normal file
BIN
packages/DocumentsUI/res/drawable-hdpi/ic_menu_search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -14,20 +14,34 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/directory"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1" />
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<FrameLayout
|
||||
android:id="@+id/directory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/roots_list"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:background="#fff" />
|
||||
|
||||
</android.support.v4.widget.DrawerLayout>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/chip"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="24dip"
|
||||
android:layout_height="24dip"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@null" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
60
packages/DocumentsUI/res/layout/item_root.xml
Normal file
60
packages/DocumentsUI/res/layout/item_root.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="@android:dimen/app_icon_size"
|
||||
android:layout_height="@android:dimen/app_icon_size"
|
||||
android:layout_rowSpan="2"
|
||||
android:layout_marginEnd="8dip"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@null" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
41
packages/DocumentsUI/res/layout/item_title.xml
Normal file
41
packages/DocumentsUI/res/layout/item_title.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -19,5 +19,12 @@
|
||||
android:id="@+id/menu_create_dir"
|
||||
android:title="@string/menu_create_dir"
|
||||
android:icon="@drawable/ic_menu_create_dir"
|
||||
android:showAsAction="always" />
|
||||
android:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/menu_search"
|
||||
android:title="@string/menu_search"
|
||||
android:icon="@drawable/ic_menu_search"
|
||||
android:showAsAction="always|collapseActionView"
|
||||
android:actionViewClass="android.widget.SearchView"
|
||||
android:imeOptions="actionSearch" />
|
||||
</menu>
|
||||
|
||||
@@ -25,9 +25,4 @@
|
||||
android:title="@string/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:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
||||
@@ -17,20 +17,24 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_label">Documents</string>
|
||||
|
||||
<string name="title_open">Open</string>
|
||||
<string name="title_save">Save</string>
|
||||
<string name="title_open">Open from</string>
|
||||
<string name="title_save">Save to</string>
|
||||
|
||||
<string name="menu_create_dir">Create folder</string>
|
||||
<string name="menu_grid">Grid view</string>
|
||||
<string name="menu_list">List view</string>
|
||||
<string name="menu_sort">Sort by</string>
|
||||
<string name="menu_search">Search</string>
|
||||
|
||||
<string name="menu_open">Open</string>
|
||||
<string name="menu_save">Save</string>
|
||||
|
||||
<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>
|
||||
<string name="sort_name">By name</string>
|
||||
<string name="sort_date">By date modified</string>
|
||||
|
||||
<string name="drawer_open">Open navigation drawer</string>
|
||||
<string name="drawer_close">Close navigation drawer</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* 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.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.RootColumns;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Display all known storage roots.
|
||||
*/
|
||||
public class BackendFragment extends Fragment {
|
||||
|
||||
// TODO: cluster backends by type
|
||||
|
||||
private GridView mGridView;
|
||||
private BackendAdapter mAdapter;
|
||||
|
||||
public static void show(FragmentManager fm) {
|
||||
final BackendFragment fragment = new BackendFragment();
|
||||
|
||||
final FragmentTransaction ft = fm.beginTransaction();
|
||||
ft.replace(R.id.directory, fragment);
|
||||
ft.setBreadCrumbTitle("TOP");
|
||||
ft.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
public static class Root {
|
||||
public int rootType;
|
||||
public Uri uri;
|
||||
public Drawable icon;
|
||||
public String title;
|
||||
public String summary;
|
||||
|
||||
public static Root fromCursor(Context context, ProviderInfo info, Cursor cursor) {
|
||||
final Root root = new Root();
|
||||
|
||||
root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE));
|
||||
root.uri = DocumentsContract.buildDocumentUri(
|
||||
info.authority, cursor.getString(cursor.getColumnIndex(RootColumns.GUID)));
|
||||
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final int icon = cursor.getInt(cursor.getColumnIndex(RootColumns.ICON));
|
||||
if (icon != 0) {
|
||||
try {
|
||||
root.icon = pm.getResourcesForApplication(info.applicationInfo)
|
||||
.getDrawable(icon);
|
||||
} catch (NotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
root.icon = info.loadIcon(pm);
|
||||
}
|
||||
|
||||
root.title = cursor.getString(cursor.getColumnIndex(RootColumns.TITLE));
|
||||
if (root.title == null) {
|
||||
root.title = info.loadLabel(pm).toString();
|
||||
}
|
||||
|
||||
root.summary = cursor.getString(cursor.getColumnIndex(RootColumns.SUMMARY));
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final Context context = inflater.getContext();
|
||||
|
||||
// Gather roots from known storage providers
|
||||
final List<ProviderInfo> providers = context.getPackageManager()
|
||||
.queryContentProviders(null, -1, PackageManager.GET_META_DATA);
|
||||
final List<Root> roots = Lists.newArrayList();
|
||||
for (ProviderInfo info : providers) {
|
||||
if (info.metaData != null
|
||||
&& info.metaData.containsKey(DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
|
||||
// TODO: populate roots on background thread, and cache results
|
||||
final Uri uri = DocumentsContract.buildRootsUri(info.authority);
|
||||
final Cursor cursor = context.getContentResolver()
|
||||
.query(uri, null, null, null, null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
roots.add(Root.fromCursor(context, info, cursor));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final View view = inflater.inflate(R.layout.fragment_backend, container, false);
|
||||
|
||||
mGridView = (GridView) view.findViewById(R.id.grid);
|
||||
mGridView.setOnItemClickListener(mItemListener);
|
||||
|
||||
mAdapter = new BackendAdapter(context, roots);
|
||||
mGridView.setAdapter(mAdapter);
|
||||
mGridView.setNumColumns(GridView.AUTO_FIT);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private OnItemClickListener mItemListener = new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
final Root root = mAdapter.getItem(position);
|
||||
((DocumentsActivity) getActivity()).onRootPicked(root);
|
||||
}
|
||||
};
|
||||
|
||||
public static class BackendAdapter extends ArrayAdapter<Root> {
|
||||
public BackendAdapter(Context context, List<Root> list) {
|
||||
super(context, android.R.layout.simple_list_item_1, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_backend, parent, false);
|
||||
}
|
||||
|
||||
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
|
||||
final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
|
||||
final PackageManager pm = parent.getContext().getPackageManager();
|
||||
final Root root = getItem(position);
|
||||
icon.setImageDrawable(root.icon);
|
||||
text1.setText(root.title);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,12 @@
|
||||
|
||||
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;
|
||||
@@ -65,8 +60,6 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
// TODO: show storage backend in item views when requested
|
||||
|
||||
private static final String TAG_SORT = "sort";
|
||||
|
||||
private ListView mListView;
|
||||
private GridView mGridView;
|
||||
|
||||
@@ -81,7 +74,8 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
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 addToBackStack) {
|
||||
final Bundle args = new Bundle();
|
||||
args.putParcelable(EXTRA_URI, uri);
|
||||
|
||||
@@ -90,7 +84,9 @@ public class DirectoryFragment extends Fragment {
|
||||
|
||||
final FragmentTransaction ft = fm.beginTransaction();
|
||||
ft.replace(R.id.directory, fragment);
|
||||
ft.addToBackStack(displayName);
|
||||
if (addToBackStack) {
|
||||
ft.addToBackStack(displayName);
|
||||
}
|
||||
ft.setBreadCrumbTitle(displayName);
|
||||
ft.commitAllowingStateLoss();
|
||||
}
|
||||
@@ -136,7 +132,13 @@ public class DirectoryFragment extends Fragment {
|
||||
sortOrder = null;
|
||||
}
|
||||
|
||||
final Uri contentsUri = DocumentsContract.buildContentsUri(uri);
|
||||
final Uri contentsUri;
|
||||
if (uri.getQueryParameter(DocumentsContract.PARAM_QUERY) != null) {
|
||||
contentsUri = uri;
|
||||
} else {
|
||||
contentsUri = DocumentsContract.buildContentsUri(uri);
|
||||
}
|
||||
|
||||
return new CursorLoader(context, contentsUri, null, null, null, sortOrder);
|
||||
}
|
||||
|
||||
@@ -198,9 +200,6 @@ public class DirectoryFragment extends Fragment {
|
||||
updateMode();
|
||||
getFragmentManager().invalidateOptionsMenu();
|
||||
return true;
|
||||
} else if (id == R.id.menu_sort) {
|
||||
SortFragment.show(this);
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@@ -238,7 +237,7 @@ public class DirectoryFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSortBy() {
|
||||
public void updateSortBy() {
|
||||
getLoaderManager().restartLoader(LOADER_DOCUMENTS, getArguments(), mCallbacks);
|
||||
}
|
||||
|
||||
@@ -358,38 +357,6 @@ 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);
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentManager.BackStackEntry;
|
||||
import android.app.FragmentManager.OnBackStackChangedListener;
|
||||
import android.content.ClipData;
|
||||
import android.content.ContentResolver;
|
||||
@@ -35,25 +34,42 @@ import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.DocumentColumns;
|
||||
import android.provider.DocumentsContract.RootColumns;
|
||||
import android.support.v4.app.ActionBarDrawerToggle;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v4.widget.DrawerLayout.DrawerListener;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.SearchView.OnQueryTextListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.documentsui.BackendFragment.Root;
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -63,18 +79,30 @@ public class DocumentsActivity extends Activity {
|
||||
// TODO: fragment to show recently opened documents
|
||||
// TODO: pull actionbar icon from current backend
|
||||
|
||||
private static final String TAG_CREATE_DIRECTORY = "create_directory";
|
||||
|
||||
private static final int ACTION_OPEN = 1;
|
||||
private static final int ACTION_CREATE = 2;
|
||||
|
||||
private int mAction;
|
||||
private String[] mAcceptMimes;
|
||||
|
||||
private SearchView mSearchView;
|
||||
|
||||
private DrawerLayout mDrawerLayout;
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
|
||||
private ArrayList<Root> mRoots = Lists.newArrayList();
|
||||
private RootsAdapter mRootsAdapter;
|
||||
private ListView mRootsList;
|
||||
|
||||
private final DisplayState mDisplayState = new DisplayState();
|
||||
|
||||
private boolean mIgnoreNextNavigation;
|
||||
private Root mCurrentRoot;
|
||||
|
||||
private Uri mCurrentDir;
|
||||
private boolean mCurrentSupportsCreate;
|
||||
private boolean mCurrentSupportsSearch;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
@@ -106,40 +134,91 @@ public class DocumentsActivity extends Activity {
|
||||
setContentView(R.layout.activity);
|
||||
|
||||
getFragmentManager().addOnBackStackChangedListener(mStackListener);
|
||||
BackendFragment.show(getFragmentManager());
|
||||
|
||||
updateActionBar();
|
||||
|
||||
if (mAction == ACTION_CREATE) {
|
||||
final String mimeType = getIntent().getType();
|
||||
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
|
||||
SaveFragment.show(getFragmentManager(), mimeType, title);
|
||||
}
|
||||
|
||||
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
mRootsAdapter = new RootsAdapter(this, mRoots);
|
||||
mRootsList = (ListView) findViewById(R.id.roots_list);
|
||||
mRootsList.setAdapter(mRootsAdapter);
|
||||
mRootsList.setOnItemClickListener(mRootsListener);
|
||||
|
||||
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
|
||||
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
|
||||
|
||||
mDrawerLayout.setDrawerListener(mDrawerListener);
|
||||
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
||||
|
||||
mDrawerLayout.openDrawer(mRootsList);
|
||||
|
||||
updateActionBar();
|
||||
updateRoots();
|
||||
}
|
||||
|
||||
private DrawerListener mDrawerListener = new DrawerListener() {
|
||||
@Override
|
||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||
mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
mDrawerToggle.onDrawerOpened(drawerView);
|
||||
updateActionBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
mDrawerToggle.onDrawerClosed(drawerView);
|
||||
updateActionBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerStateChanged(int newState) {
|
||||
mDrawerToggle.onDrawerStateChanged(newState);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
mDrawerToggle.syncState();
|
||||
}
|
||||
|
||||
public void updateActionBar() {
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
final ActionBar actionBar = getActionBar();
|
||||
|
||||
if (fm.getBackStackEntryCount() > 0) {
|
||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||
actionBar.setDisplayShowHomeEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(null);
|
||||
actionBar.setListNavigationCallbacks(mStackAdapter, mNavigationListener);
|
||||
actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
|
||||
mIgnoreNextNavigation = true;
|
||||
actionBar.setDisplayShowHomeEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
} else {
|
||||
if (mDrawerLayout.isDrawerOpen(mRootsList)) {
|
||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
||||
actionBar.setDisplayShowHomeEnabled(false);
|
||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||
actionBar.setIcon(new ColorDrawable());
|
||||
|
||||
if (mAction == ACTION_OPEN) {
|
||||
actionBar.setTitle(R.string.title_open);
|
||||
} else if (mAction == ACTION_CREATE) {
|
||||
actionBar.setTitle(R.string.title_save);
|
||||
}
|
||||
|
||||
} else {
|
||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||
if (mCurrentRoot != null) {
|
||||
actionBar.setIcon(mCurrentRoot.icon);
|
||||
}
|
||||
actionBar.setTitle(null);
|
||||
actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener);
|
||||
|
||||
if (fm.getBackStackEntryCount() > 0) {
|
||||
mDrawerToggle.setDrawerIndicatorEnabled(false);
|
||||
} else {
|
||||
mDrawerToggle.setDrawerIndicatorEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +226,25 @@ public class DocumentsActivity extends Activity {
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
getMenuInflater().inflate(R.menu.activity, menu);
|
||||
|
||||
final MenuItem searchMenu = menu.findItem(R.id.menu_search);
|
||||
mSearchView = (SearchView) searchMenu.getActionView();
|
||||
mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
// TODO: clear existing directory stack?
|
||||
final Uri searchUri = DocumentsContract.buildSearchUri(mCurrentDir, query);
|
||||
DirectoryFragment.show(getFragmentManager(), searchUri, query, true);
|
||||
mSearchView.setIconified(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -158,17 +256,29 @@ public class DocumentsActivity extends Activity {
|
||||
createDir.setVisible(mAction == ACTION_CREATE);
|
||||
createDir.setEnabled(mCurrentSupportsCreate);
|
||||
|
||||
// TODO: close any search in-progress when hiding
|
||||
final MenuItem search = menu.findItem(R.id.menu_search);
|
||||
search.setVisible(mCurrentSupportsSearch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (mDrawerToggle.onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
getFragmentManager().popBackStack();
|
||||
updateActionBar();
|
||||
return true;
|
||||
} else if (id == R.id.menu_create_dir) {
|
||||
CreateDirectoryFragment.show(getFragmentManager());
|
||||
return true;
|
||||
} else if (id == R.id.menu_search) {
|
||||
return false;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@@ -180,46 +290,73 @@ public class DocumentsActivity extends Activity {
|
||||
}
|
||||
};
|
||||
|
||||
private BaseAdapter mStackAdapter = new BaseAdapter() {
|
||||
// TODO: support additional sort orders
|
||||
private BaseAdapter mSortAdapter = new BaseAdapter() {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return getFragmentManager().getBackStackEntryCount();
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return getFragmentManager().getBackStackEntryAt(position);
|
||||
switch (position) {
|
||||
case 0:
|
||||
return getText(R.string.sort_name);
|
||||
case 1:
|
||||
return getText(R.string.sort_date);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getFragmentManager().getBackStackEntryAt(position).getId();
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_title, parent, false);
|
||||
}
|
||||
|
||||
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
|
||||
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
|
||||
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
final int count = fm.getBackStackEntryCount();
|
||||
if (count > 0) {
|
||||
title.setText(fm.getBackStackEntryAt(count - 1).getBreadCrumbTitle());
|
||||
} else if (mCurrentRoot != null) {
|
||||
title.setText(mCurrentRoot.title);
|
||||
} else {
|
||||
title.setText(null);
|
||||
}
|
||||
|
||||
summary.setText((String) getItem(position));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
|
||||
}
|
||||
|
||||
final BackStackEntry entry = getFragmentManager().getBackStackEntryAt(position);
|
||||
final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
text1.setText(entry.getBreadCrumbTitle());
|
||||
text1.setText((String) getItem(position));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
};
|
||||
|
||||
private OnNavigationListener mNavigationListener = new OnNavigationListener() {
|
||||
private OnNavigationListener mSortListener = new OnNavigationListener() {
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
||||
if (mIgnoreNextNavigation) {
|
||||
mIgnoreNextNavigation = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
getFragmentManager().popBackStack((int) itemId, 0);
|
||||
// TODO: request updated sort order
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -231,6 +368,7 @@ public class DocumentsActivity extends Activity {
|
||||
public void onDirectoryChanged(Uri uri, int flags) {
|
||||
mCurrentDir = uri;
|
||||
mCurrentSupportsCreate = (flags & DocumentsContract.FLAG_SUPPORTS_CREATE) != 0;
|
||||
mCurrentSupportsSearch = (flags & DocumentsContract.FLAG_SUPPORTS_SEARCH) != 0;
|
||||
|
||||
if (mAction == ACTION_CREATE) {
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
@@ -240,15 +378,11 @@ public class DocumentsActivity extends Activity {
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
public void onRootPicked(Root root) {
|
||||
DirectoryFragment.show(getFragmentManager(), root.uri, root.title);
|
||||
}
|
||||
|
||||
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);
|
||||
DirectoryFragment.show(fm, doc.uri, doc.displayName, true);
|
||||
} else if (mAction == ACTION_OPEN) {
|
||||
// Explicit file picked, return
|
||||
onFinished(doc.uri);
|
||||
@@ -313,6 +447,46 @@ public class DocumentsActivity extends Activity {
|
||||
public static final int SORT_BY_DATE = 1;
|
||||
}
|
||||
|
||||
public static class Root {
|
||||
public int rootType;
|
||||
public Uri uri;
|
||||
public Drawable icon;
|
||||
public String title;
|
||||
public String summary;
|
||||
|
||||
public static Root fromCursor(Context context, ProviderInfo info, Cursor cursor) {
|
||||
final Root root = new Root();
|
||||
|
||||
root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE));
|
||||
root.uri = DocumentsContract.buildDocumentUri(
|
||||
info.authority, cursor.getString(cursor.getColumnIndex(RootColumns.GUID)));
|
||||
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final int icon = cursor.getInt(cursor.getColumnIndex(RootColumns.ICON));
|
||||
if (icon != 0) {
|
||||
try {
|
||||
root.icon = pm.getResourcesForApplication(info.applicationInfo)
|
||||
.getDrawable(icon);
|
||||
} catch (NotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
root.icon = info.loadIcon(pm);
|
||||
}
|
||||
|
||||
root.title = cursor.getString(cursor.getColumnIndex(RootColumns.TITLE));
|
||||
if (root.title == null) {
|
||||
root.title = info.loadLabel(pm).toString();
|
||||
}
|
||||
|
||||
root.summary = cursor.getString(cursor.getColumnIndex(RootColumns.SUMMARY));
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Document {
|
||||
public Uri uri;
|
||||
public String mimeType;
|
||||
@@ -386,7 +560,74 @@ public class DocumentsActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG_CREATE_DIRECTORY = "create_directory";
|
||||
/**
|
||||
* Gather roots from all known storage providers.
|
||||
*/
|
||||
private void updateRoots() {
|
||||
mRoots.clear();
|
||||
|
||||
final List<ProviderInfo> providers = getPackageManager()
|
||||
.queryContentProviders(null, -1, PackageManager.GET_META_DATA);
|
||||
for (ProviderInfo info : providers) {
|
||||
if (info.metaData != null
|
||||
&& info.metaData.containsKey(DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
|
||||
// TODO: populate roots on background thread, and cache results
|
||||
final Uri uri = DocumentsContract.buildRootsUri(info.authority);
|
||||
final Cursor cursor = getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
mRoots.add(Root.fromCursor(this, info, cursor));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OnItemClickListener mRootsListener = new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// Clear entire backstack and start in new root
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
while (fm.getBackStackEntryCount() > 0) {
|
||||
fm.popBackStackImmediate();
|
||||
}
|
||||
|
||||
mCurrentRoot = mRootsAdapter.getItem(position);
|
||||
DirectoryFragment.show(
|
||||
getFragmentManager(), mCurrentRoot.uri, mCurrentRoot.title, false);
|
||||
|
||||
mDrawerLayout.closeDrawers();
|
||||
}
|
||||
};
|
||||
|
||||
public static class RootsAdapter extends ArrayAdapter<Root> {
|
||||
public RootsAdapter(Context context, List<Root> list) {
|
||||
super(context, android.R.layout.simple_list_item_1, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_root, parent, false);
|
||||
}
|
||||
|
||||
final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
|
||||
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
|
||||
final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
|
||||
|
||||
final Root root = getItem(position);
|
||||
icon.setImageDrawable(root.icon);
|
||||
title.setText(root.title);
|
||||
|
||||
summary.setText(root.summary);
|
||||
summary.setVisibility(root.summary != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CreateDirectoryFragment extends DialogFragment {
|
||||
public static void show(FragmentManager fm) {
|
||||
|
||||
Reference in New Issue
Block a user