summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/content/res/XmlResourceParser.java2
-rw-r--r--core/res/res/values/attrs.xml5
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--packages/DocumentsUI/res/values/strings.xml6
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java9
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java192
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java2
-rw-r--r--packages/ExternalStorageProvider/AndroidManifest.xml2
-rw-r--r--packages/ExternalStorageProvider/res/drawable-hdpi/ic_pdf.pngbin0 -> 1326 bytes
-rw-r--r--packages/ExternalStorageProvider/res/xml/document_provider.xml22
11 files changed, 202 insertions, 42 deletions
diff --git a/api/current.txt b/api/current.txt
index 620e5412d99d..944b1c24c127 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -387,6 +387,7 @@ package android {
field public static final int cropToPadding = 16843043; // 0x1010123
field public static final int cursorVisible = 16843090; // 0x1010152
field public static final int customNavigationLayout = 16843474; // 0x10102d2
+ field public static final int customRoots = 16843751; // 0x10103e7
field public static final int customTokens = 16843579; // 0x101033b
field public static final int cycles = 16843220; // 0x10101d4
field public static final int dashGap = 16843175; // 0x10101a7
@@ -7624,7 +7625,7 @@ package android.content.res {
method public void recycle();
}
- public abstract interface XmlResourceParser implements android.util.AttributeSet org.xmlpull.v1.XmlPullParser {
+ public abstract interface XmlResourceParser implements android.util.AttributeSet java.lang.AutoCloseable org.xmlpull.v1.XmlPullParser {
method public abstract void close();
}
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
index c59e6d4e0e01..5af49d4d46a2 100644
--- a/core/java/android/content/res/XmlResourceParser.java
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -26,7 +26,7 @@ import android.util.AttributeSet;
* an additional close() method on this interface for the client to indicate
* when it is done reading the resource.
*/
-public interface XmlResourceParser extends XmlPullParser, AttributeSet {
+public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
/**
* Close this interface to the resource. Calls on the interface are no
* longer value after this call.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4cad23231b59..2096f6635c76 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6007,4 +6007,9 @@
<attr name="digit" format="integer" />
<attr name="textView" format="reference" />
</declare-styleable>
+
+ <declare-styleable name="DocumentsProviderInfo">
+ <attr name="customRoots" format="boolean" />
+ </declare-styleable>
+
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b9c36382ddaf..0eaab656cae0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2070,4 +2070,5 @@
<public type="attr" name="vendor" />
<public type="attr" name="category" />
<public type="attr" name="isAsciiCapable" />
+ <public type="attr" name="customRoots" />
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 18e486dc9519..665f3b13628d 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -34,7 +34,9 @@
<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>
+ <string name="drawer_open">Show roots</string>
+ <string name="drawer_close">Hide roots</string>
+
+ <string name="save_error">Failed to save document</string>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 8b3dd99179c3..1f22613a4bd8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -342,12 +342,15 @@ public class DirectoryFragment extends Fragment {
final long lastModified = getCursorLong(cursor, DocumentColumns.LAST_MODIFIED);
final int flags = getCursorInt(cursor, DocumentColumns.FLAGS);
+ final Uri uri = getArguments().getParcelable(EXTRA_URI);
+ final String authority = uri.getAuthority();
+
if ((flags & DocumentsContract.FLAG_SUPPORTS_THUMBNAIL) != 0) {
- final Uri uri = getArguments().getParcelable(EXTRA_URI);
- final Uri childUri = DocumentsContract.buildDocumentUri(uri.getAuthority(), guid);
+ final Uri childUri = DocumentsContract.buildDocumentUri(authority, guid);
icon.setImageURI(childUri);
} else {
- icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(context, mimeType));
+ icon.setImageDrawable(
+ DocumentsActivity.resolveDocumentIcon(context, authority, mimeType));
}
title.setText(displayName);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 13def57e5931..dcd02d24a4a1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -37,7 +37,10 @@ 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;
import android.content.res.Resources.NotFoundException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -50,7 +53,9 @@ 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.AttributeSet;
import android.util.Log;
+import android.util.Xml;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -66,11 +71,20 @@ import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
+import android.widget.Toast;
import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
public class DocumentsActivity extends Activity {
@@ -92,7 +106,9 @@ public class DocumentsActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
- private ArrayList<Root> mRoots = Lists.newArrayList();
+ private static HashMap<String, DocumentsProviderInfo> sProviders = Maps.newHashMap();
+ private static ArrayList<Root> sRoots = Lists.newArrayList();
+
private RootsAdapter mRootsAdapter;
private ListView mRootsList;
@@ -142,7 +158,7 @@ public class DocumentsActivity extends Activity {
}
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- mRootsAdapter = new RootsAdapter(this, mRoots);
+ mRootsAdapter = new RootsAdapter(this, sRoots);
mRootsList = (ListView) findViewById(R.id.roots_list);
mRootsList.setAdapter(mRootsAdapter);
mRootsList.setOnItemClickListener(mRootsListener);
@@ -406,9 +422,13 @@ public class DocumentsActivity extends Activity {
values.put(DocumentColumns.MIME_TYPE, mimeType);
values.put(DocumentColumns.DISPLAY_NAME, displayName);
- // TODO: handle errors from remote side
final Uri uri = getContentResolver().insert(mCurrentDir, values);
- onFinished(uri);
+ if (uri != null) {
+ onFinished(uri);
+ } else {
+ // TODO: ask for overwrite confirmation
+ Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
+ }
}
private void onFinished(Uri... uris) {
@@ -448,37 +468,52 @@ public class DocumentsActivity extends Activity {
}
public static class Root {
+ public DocumentsProviderInfo info;
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) {
+ public static Root fromInfo(Context context, DocumentsProviderInfo info) {
final Root root = new Root();
+ final PackageManager pm = context.getPackageManager();
- root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE));
+ root.info = info;
+ root.rootType = DocumentsContract.ROOT_TYPE_SERVICE;
root.uri = DocumentsContract.buildDocumentUri(
- info.authority, cursor.getString(cursor.getColumnIndex(RootColumns.GUID)));
+ info.providerInfo.authority, DocumentsContract.ROOT_GUID);
+ root.icon = info.providerInfo.loadIcon(pm);
+ root.title = info.providerInfo.loadLabel(pm).toString();
+ root.summary = null;
+
+ return root;
+ }
+
+ public static Root fromCursor(
+ Context context, DocumentsProviderInfo info, Cursor cursor) {
+ final Root root = fromInfo(context, info);
+
+ root.rootType = cursor.getInt(cursor.getColumnIndex(RootColumns.ROOT_TYPE));
+ root.uri = DocumentsContract.buildDocumentUri(info.providerInfo.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)
+ root.icon = pm.getResourcesForApplication(info.providerInfo.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();
+ final String title = cursor.getString(cursor.getColumnIndex(RootColumns.TITLE));
+ if (title != null) {
+ root.title = title;
}
root.summary = cursor.getString(cursor.getColumnIndex(RootColumns.SUMMARY));
@@ -487,6 +522,17 @@ public class DocumentsActivity extends Activity {
}
}
+ public static class DocumentsProviderInfo {
+ public ProviderInfo providerInfo;
+ public boolean customRoots;
+ public List<Icon> customIcons;
+ }
+
+ public static class Icon {
+ public String mimeType;
+ public Drawable icon;
+ }
+
public static class Document {
public Uri uri;
public String mimeType;
@@ -541,8 +587,17 @@ public class DocumentsActivity extends Activity {
}
}
- public static Drawable resolveDocumentIcon(Context context, String mimeType) {
- // TODO: allow backends to provide custom MIME icons
+ public static Drawable resolveDocumentIcon(Context context, String authority, String mimeType) {
+ // Custom icons take precedence
+ final DocumentsProviderInfo info = sProviders.get(authority);
+ if (info != null) {
+ for (Icon icon : info.customIcons) {
+ if (mimeMatches(icon.mimeType, mimeType)) {
+ return icon.icon;
+ }
+ }
+ }
+
if (DocumentsContract.MIME_TYPE_DIRECTORY.equals(mimeType)) {
return context.getResources().getDrawable(R.drawable.ic_dir);
} else {
@@ -550,39 +605,110 @@ public class DocumentsActivity extends Activity {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType(mimeType);
- final ResolveInfo info = pm.resolveActivity(
+ final ResolveInfo activityInfo = pm.resolveActivity(
intent, PackageManager.MATCH_DEFAULT_ONLY);
- if (info != null) {
- return info.loadIcon(pm);
+ if (activityInfo != null) {
+ return activityInfo.loadIcon(pm);
} else {
return null;
}
}
}
+ private static final String TAG_DOCUMENTS_PROVIDER = "documents-provider";
+ private static final String TAG_ICON = "icon";
+
/**
* 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));
+ sProviders.clear();
+ sRoots.clear();
+
+ final PackageManager pm = getPackageManager();
+ final List<ProviderInfo> providers = pm.queryContentProviders(
+ null, -1, PackageManager.GET_META_DATA);
+ for (ProviderInfo providerInfo : providers) {
+ if (providerInfo.metaData != null && providerInfo.metaData.containsKey(
+ DocumentsContract.META_DATA_DOCUMENT_PROVIDER)) {
+ final DocumentsProviderInfo info = parseInfo(this, providerInfo);
+ if (info == null) {
+ Log.w(TAG, "Missing info for " + providerInfo);
+ continue;
+ }
+
+ sProviders.put(info.providerInfo.authority, info);
+
+ if (info.customRoots) {
+ // TODO: populate roots on background thread, and cache results
+ final Uri uri = DocumentsContract.buildRootsUri(providerInfo.authority);
+ final Cursor cursor = getContentResolver().query(uri, null, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ sRoots.add(Root.fromCursor(this, info, cursor));
+ }
+ } finally {
+ cursor.close();
}
- } finally {
- cursor.close();
+ } else if (info != null) {
+ sRoots.add(Root.fromInfo(this, info));
+ }
+ }
+ }
+ }
+
+ private static DocumentsProviderInfo parseInfo(Context context, ProviderInfo providerInfo) {
+ final DocumentsProviderInfo info = new DocumentsProviderInfo();
+ info.providerInfo = providerInfo;
+ info.customIcons = Lists.newArrayList();
+
+ final PackageManager pm = context.getPackageManager();
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(providerInfo.applicationInfo);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Failed to find resources for " + providerInfo, e);
+ return null;
+ }
+
+ XmlResourceParser parser = null;
+ try {
+ parser = providerInfo.loadXmlMetaData(
+ pm, DocumentsContract.META_DATA_DOCUMENT_PROVIDER);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type = 0;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ final String tag = parser.getName();
+ if (type == XmlPullParser.START_TAG && TAG_DOCUMENTS_PROVIDER.equals(tag)) {
+ final TypedArray a = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.DocumentsProviderInfo);
+ info.customRoots = a.getBoolean(
+ com.android.internal.R.styleable.DocumentsProviderInfo_customRoots,
+ false);
+ a.recycle();
+
+ } else if (type == XmlPullParser.START_TAG && TAG_ICON.equals(tag)) {
+ final TypedArray a = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.Icon);
+ final Icon icon = new Icon();
+ icon.mimeType = a.getString(com.android.internal.R.styleable.Icon_mimeType);
+ icon.icon = a.getDrawable(com.android.internal.R.styleable.Icon_icon);
+ info.customIcons.add(icon);
+ a.recycle();
}
}
+ } catch (IOException e){
+ Log.w(TAG, "Failed to parse metadata", e);
+ return null;
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Failed to parse metadata", e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(parser);
}
+
+ return info;
}
private OnItemClickListener mRootsListener = new OnItemClickListener() {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index a2a4f7cb285f..cdc399d00a6a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -66,7 +66,7 @@ public class SaveFragment extends Fragment {
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
icon.setImageDrawable(DocumentsActivity.resolveDocumentIcon(
- context, getArguments().getString(EXTRA_MIME_TYPE)));
+ context, null, getArguments().getString(EXTRA_MIME_TYPE)));
mDisplayName = (EditText) view.findViewById(android.R.id.title);
mDisplayName.setText(getArguments().getString(EXTRA_DISPLAY_NAME));
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 37dc5b101e4b..afdb6bb0be24 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -13,7 +13,7 @@
android:permission="android.permission.MANAGE_DOCUMENTS">
<meta-data
android:name="android.content.DOCUMENT_PROVIDER"
- android:value="true" />
+ android:resource="@xml/document_provider" />
</provider>
</application>
</manifest>
diff --git a/packages/ExternalStorageProvider/res/drawable-hdpi/ic_pdf.png b/packages/ExternalStorageProvider/res/drawable-hdpi/ic_pdf.png
new file mode 100644
index 000000000000..961a9bbfcfcf
--- /dev/null
+++ b/packages/ExternalStorageProvider/res/drawable-hdpi/ic_pdf.png
Binary files differ
diff --git a/packages/ExternalStorageProvider/res/xml/document_provider.xml b/packages/ExternalStorageProvider/res/xml/document_provider.xml
new file mode 100644
index 000000000000..929a2734a0f2
--- /dev/null
+++ b/packages/ExternalStorageProvider/res/xml/document_provider.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<documents-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:customRoots="true">
+
+ <icon android:mimeType="application/pdf" android:icon="@drawable/ic_pdf" />
+ <icon android:mimeType="text/*" android:icon="@drawable/ic_pdf" />
+</documents-provider>