Merge "Sliding drawer for roots, move sorting, search."
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