summaryrefslogtreecommitdiffstats
path: root/packages/DocumentsUI
diff options
context:
space:
mode:
Diffstat (limited to 'packages/DocumentsUI')
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml8
-rw-r--r--packages/DocumentsUI/res/layout/fragment_save.xml32
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java7
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java23
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java21
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java1
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java8
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java46
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java51
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java133
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsCache.java5
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java35
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java7
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java10
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java14
15 files changed, 365 insertions, 36 deletions
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 6faf7f8..179bcd1 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -50,6 +50,14 @@
android:authorities="com.android.documentsui.recents"
android:exported="false" />
+ <receiver android:name=".PackageReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+ <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+
<!-- TODO: remove when we have real clients -->
<activity android:name=".TestActivity" android:enabled="false">
<intent-filter>
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 891f0a0..d601194 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -51,15 +51,31 @@
android:singleLine="true"
android:selectAllOnFocus="true" />
- <Button
- android:id="@android:id/button1"
+ <FrameLayout
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/menu_save"
- android:textAllCaps="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:padding="8dp" />
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/menu_save"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:padding="8dp" />
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:indeterminate="true"
+ android:padding="8dp"
+ style="?android:attr/progressBarStyle" />
+
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 90be197..ba8c35f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -95,6 +95,11 @@ public class CreateDirectoryFragment extends DialogFragment {
}
@Override
+ protected void onPreExecute() {
+ mActivity.setPending(true);
+ }
+
+ @Override
protected DocumentInfo doInBackground(Void... params) {
final ContentResolver resolver = mActivity.getContentResolver();
ContentProviderClient client = null;
@@ -120,6 +125,8 @@ public class CreateDirectoryFragment extends DialogFragment {
} else {
Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
}
+
+ mActivity.setPending(false);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 1f3901c..b2b2bd8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -47,6 +47,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -76,6 +77,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.ProviderExecutor.Preemptable;
import com.android.documentsui.RecentsProvider.StateColumns;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -83,7 +85,6 @@ import com.google.android.collect.Lists;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Display the documents inside a single directory.
@@ -126,9 +127,7 @@ public class DirectoryFragment extends Fragment {
private static final String EXTRA_QUERY = "query";
private static final String EXTRA_IGNORE_STATE = "ignoreState";
- private static AtomicInteger sLoaderId = new AtomicInteger(4000);
-
- private final int mLoaderId = sLoaderId.incrementAndGet();
+ private final int mLoaderId = 42;
public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
show(fm, TYPE_NORMAL, root, doc, null, anim);
@@ -528,7 +527,7 @@ public class DirectoryFragment extends Fragment {
if (iconThumb != null) {
final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
if (oldTask != null) {
- oldTask.reallyCancel();
+ oldTask.preempt();
iconThumb.setTag(null);
}
}
@@ -794,7 +793,7 @@ public class DirectoryFragment extends Fragment {
final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) iconThumb.getTag();
if (oldTask != null) {
- oldTask.reallyCancel();
+ oldTask.preempt();
iconThumb.setTag(null);
}
@@ -818,7 +817,7 @@ public class DirectoryFragment extends Fragment {
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(
uri, iconMime, iconThumb, mThumbSize);
iconThumb.setTag(task);
- task.executeOnExecutor(ProviderExecutor.forAuthority(docAuthority));
+ ProviderExecutor.forAuthority(docAuthority).execute(task);
}
}
@@ -988,7 +987,8 @@ public class DirectoryFragment extends Fragment {
}
}
- private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap> {
+ private static class ThumbnailAsyncTask extends AsyncTask<Uri, Void, Bitmap>
+ implements Preemptable {
private final Uri mUri;
private final ImageView mIconMime;
private final ImageView mIconThumb;
@@ -1004,7 +1004,8 @@ public class DirectoryFragment extends Fragment {
mSignal = new CancellationSignal();
}
- public void reallyCancel() {
+ @Override
+ public void preempt() {
cancel(false);
mSignal.cancel();
}
@@ -1028,7 +1029,9 @@ public class DirectoryFragment extends Fragment {
thumbs.put(mUri, result);
}
} catch (Exception e) {
- Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ if (!(e instanceof OperationCanceledException)) {
+ Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ }
} finally {
ContentProviderClient.releaseQuietly(client);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index d675e8d..4212e96 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -255,7 +255,9 @@ public class DocumentsActivity extends Activity {
}
mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
- mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
+ mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ mState.showAdvanced = mState.forceAdvanced
+ | SettingsActivity.getDisplayAdvancedDevices(this);
}
private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -661,6 +663,13 @@ public class DocumentsActivity extends Activity {
DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
}
+ public void setPending(boolean pending) {
+ final SaveFragment save = SaveFragment.get(getFragmentManager());
+ if (save != null) {
+ save.setPending(pending);
+ }
+ }
+
@Override
public void onBackPressed() {
if (!mState.stackTouched) {
@@ -1051,6 +1060,11 @@ public class DocumentsActivity extends Activity {
}
@Override
+ protected void onPreExecute() {
+ setPending(true);
+ }
+
+ @Override
protected Uri doInBackground(Void... params) {
final ContentResolver resolver = getContentResolver();
final DocumentInfo cwd = getCurrentDirectory();
@@ -1083,6 +1097,8 @@ public class DocumentsActivity extends Activity {
Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
.show();
}
+
+ setPending(false);
}
}
@@ -1122,6 +1138,7 @@ public class DocumentsActivity extends Activity {
public boolean allowMultiple = false;
public boolean showSize = false;
public boolean localOnly = false;
+ public boolean forceAdvanced = false;
public boolean showAdvanced = false;
public boolean stackTouched = false;
public boolean restored = false;
@@ -1162,6 +1179,7 @@ public class DocumentsActivity extends Activity {
out.writeInt(allowMultiple ? 1 : 0);
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
+ out.writeInt(forceAdvanced ? 1 : 0);
out.writeInt(showAdvanced ? 1 : 0);
out.writeInt(stackTouched ? 1 : 0);
out.writeInt(restored ? 1 : 0);
@@ -1181,6 +1199,7 @@ public class DocumentsActivity extends Activity {
state.allowMultiple = in.readInt() != 0;
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
+ state.forceAdvanced = in.readInt() != 0;
state.showAdvanced = in.readInt() != 0;
state.stackTouched = in.readInt() != 0;
state.restored = in.readInt() != 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
index 6b46e3a..547e343 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
@@ -75,6 +75,7 @@ public class DocumentsApplication extends Application {
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
packageFilter.addDataScheme("package");
registerReceiver(mCacheReceiver, packageFilter);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 52d816f..55d73f2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -17,6 +17,8 @@
package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.TAG;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.database.AbstractCursor;
import android.database.Cursor;
@@ -50,10 +52,8 @@ public class FilteringCursorWrapper extends AbstractCursor {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
- final String mimeType = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
- final long lastModified = cursor.getLong(
- cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
+ final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) {
continue;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
new file mode 100644
index 0000000..aef63af
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/PackageReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Clean up {@link RecentsProvider} when packages are removed.
+ */
+public class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
+
+ } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+ final Uri data = intent.getData();
+ if (data != null) {
+ final String packageName = data.getSchemeSpecificPart();
+ resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
+ packageName, null);
+ }
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
index 2105cb4..f94aebd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java
@@ -16,10 +16,15 @@
package com.android.documentsui;
+import android.os.AsyncTask;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -29,7 +34,7 @@ public class ProviderExecutor extends Thread implements Executor {
@GuardedBy("sExecutors")
private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap();
- public static Executor forAuthority(String authority) {
+ public static ProviderExecutor forAuthority(String authority) {
synchronized (sExecutors) {
ProviderExecutor executor = sExecutors.get(authority);
if (executor == null) {
@@ -42,10 +47,54 @@ public class ProviderExecutor extends Thread implements Executor {
}
}
+ public interface Preemptable {
+ void preempt();
+ }
+
private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>();
+ private final ArrayList<WeakReference<Preemptable>> mPreemptable = Lists.newArrayList();
+
+ private void preempt() {
+ synchronized (mPreemptable) {
+ int count = 0;
+ for (WeakReference<Preemptable> ref : mPreemptable) {
+ final Preemptable p = ref.get();
+ if (p != null) {
+ count++;
+ p.preempt();
+ }
+ }
+ mPreemptable.clear();
+ }
+ }
+
+ /**
+ * Execute the given task. If given task is not {@link Preemptable}, it will
+ * preempt all outstanding preemptable tasks.
+ */
+ public <P> void execute(AsyncTask<P, ?, ?> task, P... params) {
+ if (task instanceof Preemptable) {
+ synchronized (mPreemptable) {
+ mPreemptable.add(new WeakReference<Preemptable>((Preemptable) task));
+ }
+ task.executeOnExecutor(mNonPreemptingExecutor, params);
+ } else {
+ task.executeOnExecutor(this, params);
+ }
+ }
+
+ private Executor mNonPreemptingExecutor = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ Preconditions.checkNotNull(command);
+ mQueue.add(command);
+ }
+ };
+
@Override
public void execute(Runnable command) {
+ preempt();
Preconditions.checkNotNull(command);
mQueue.add(command);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 4313fa7..f6e4349 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -16,24 +16,40 @@
package com.android.documentsui;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.content.UriMatcher;
+import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.text.format.DateUtils;
import android.util.Log;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.internal.util.Predicate;
+import com.google.android.collect.Sets;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.Set;
+
public class RecentsProvider extends ContentProvider {
private static final String TAG = "RecentsProvider";
- public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
+ private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
private static final String AUTHORITY = "com.android.documentsui.recents";
@@ -43,6 +59,9 @@ public class RecentsProvider extends ContentProvider {
private static final int URI_STATE = 2;
private static final int URI_RESUME = 3;
+ public static final String METHOD_PURGE = "purge";
+ public static final String METHOD_PURGE_PACKAGE = "purgePackage";
+
static {
sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
// state/authority/rootId/docId
@@ -231,4 +250,116 @@ public class RecentsProvider extends ContentProvider {
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ if (METHOD_PURGE.equals(method)) {
+ // Purge references to unknown authorities
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ final Set<String> knownAuth = Sets.newHashSet();
+ for (ResolveInfo info : getContext()
+ .getPackageManager().queryIntentContentProviders(intent, 0)) {
+ knownAuth.add(info.providerInfo.authority);
+ }
+
+ purgeByAuthority(new Predicate<String>() {
+ @Override
+ public boolean apply(String authority) {
+ // Purge unknown authorities
+ return !knownAuth.contains(authority);
+ }
+ });
+
+ return null;
+
+ } else if (METHOD_PURGE_PACKAGE.equals(method)) {
+ // Purge references to authorities in given package
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ intent.setPackage(arg);
+ final Set<String> packageAuth = Sets.newHashSet();
+ for (ResolveInfo info : getContext()
+ .getPackageManager().queryIntentContentProviders(intent, 0)) {
+ packageAuth.add(info.providerInfo.authority);
+ }
+
+ if (!packageAuth.isEmpty()) {
+ purgeByAuthority(new Predicate<String>() {
+ @Override
+ public boolean apply(String authority) {
+ // Purge authority matches
+ return packageAuth.contains(authority);
+ }
+ });
+ }
+
+ return null;
+
+ } else {
+ return super.call(method, arg, extras);
+ }
+ }
+
+ /**
+ * Purge all internal data whose authority matches the given
+ * {@link Predicate}.
+ */
+ private void purgeByAuthority(Predicate<String> predicate) {
+ final SQLiteDatabase db = mHelper.getWritableDatabase();
+ final DocumentStack stack = new DocumentStack();
+
+ Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ try {
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(RecentColumns.STACK));
+ DurableUtils.readFromArray(rawStack, stack);
+
+ if (stack.root != null && predicate.apply(stack.root.authority)) {
+ final String key = getCursorString(cursor, RecentColumns.KEY);
+ db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key });
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ cursor = db.query(TABLE_STATE, new String[] {
+ StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ final String authority = getCursorString(cursor, StateColumns.AUTHORITY);
+ if (predicate.apply(authority)) {
+ db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
+ authority });
+ Log.d(TAG, "Purged state for " + authority);
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ cursor = db.query(TABLE_RESUME, null, null, null, null, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ try {
+ final byte[] rawStack = cursor.getBlob(
+ cursor.getColumnIndex(ResumeColumns.STACK));
+ DurableUtils.readFromArray(rawStack, stack);
+
+ if (stack.root != null && predicate.apply(stack.root.authority)) {
+ final String packageName = getCursorString(
+ cursor, ResumeColumns.PACKAGE_NAME);
+ db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?",
+ new String[] { packageName });
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index b98e1ee..f6b43c7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -60,8 +60,8 @@ import java.util.concurrent.TimeUnit;
public class RootsCache {
private static final boolean LOGD = true;
- // TODO: cache roots in local provider to avoid spinning up backends
- // TODO: root updates should trigger UI refresh
+ public static final Uri sNotificationUri = Uri.parse(
+ "content://com.android.documentsui.roots/");
private final Context mContext;
private final ContentObserver mObserver;
@@ -201,6 +201,7 @@ public class RootsCache {
mStoppedAuthorities = mTaskStoppedAuthorities;
}
mFirstLoad.countDown();
+ resolver.notifyChange(sNotificationUri, null, false);
return null;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index fdbc3ab..931dac9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
+
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -25,7 +27,9 @@ import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.LayoutInflater;
@@ -33,6 +37,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
@@ -131,7 +136,15 @@ public class RootsFragment extends Fragment {
final Context context = getActivity();
final State state = ((DocumentsActivity) context).getDisplayState();
- state.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(context);
+ state.showAdvanced = state.forceAdvanced
+ | SettingsActivity.getDisplayAdvancedDevices(context);
+
+ if (state.action == ACTION_GET_CONTENT) {
+ mList.setOnItemLongClickListener(mItemLongClickListener);
+ } else {
+ mList.setOnItemLongClickListener(null);
+ mList.setLongClickable(false);
+ }
getLoaderManager().restartLoader(2, null, mCallbacks);
}
@@ -152,6 +165,13 @@ public class RootsFragment extends Fragment {
}
}
+ private void showAppDetails(ResolveInfo ri) {
+ final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.fromParts("package", ri.activityInfo.packageName, null));
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ startActivity(intent);
+ }
+
private OnItemClickListener mItemListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -167,6 +187,19 @@ public class RootsFragment extends Fragment {
}
};
+ private OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ final Item item = mAdapter.getItem(position);
+ if (item instanceof AppItem) {
+ showAppDetails(((AppItem) item).info);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+
private static abstract class Item {
private final int mLayoutId;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
index 7108971..8d37cdf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
@@ -25,6 +25,8 @@ import com.android.documentsui.model.RootInfo;
import java.util.Collection;
public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
+ private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
+
private final RootsCache mRoots;
private final State mState;
@@ -34,6 +36,9 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
super(context);
mRoots = roots;
mState = state;
+
+ getContext().getContentResolver()
+ .registerContentObserver(RootsCache.sNotificationUri, false, mObserver);
}
@Override
@@ -77,5 +82,7 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
onStopLoading();
mResult = null;
+
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index 23e047c..9d70c51 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -30,6 +30,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.ProgressBar;
import com.android.documentsui.model.DocumentInfo;
@@ -42,6 +43,7 @@ public class SaveFragment extends Fragment {
private DocumentInfo mReplaceTarget;
private EditText mDisplayName;
private Button mSave;
+ private ProgressBar mProgress;
private boolean mIgnoreNextEdit;
private static final String EXTRA_MIME_TYPE = "mime_type";
@@ -83,6 +85,8 @@ public class SaveFragment extends Fragment {
mSave.setOnClickListener(mSaveListener);
mSave.setEnabled(false);
+ mProgress = (ProgressBar) view.findViewById(android.R.id.progress);
+
return view;
}
@@ -92,7 +96,6 @@ public class SaveFragment extends Fragment {
if (mIgnoreNextEdit) {
mIgnoreNextEdit = false;
} else {
- Log.d(TAG, "onTextChanged!");
mReplaceTarget = null;
}
}
@@ -140,4 +143,9 @@ public class SaveFragment extends Fragment {
public void setSaveEnabled(boolean enabled) {
mSave.setEnabled(enabled);
}
+
+ public void setPending(boolean pending) {
+ mSave.setVisibility(pending ? View.INVISIBLE : View.VISIBLE);
+ mProgress.setVisibility(pending ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 19ad2e2..a23dd15 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -19,6 +19,8 @@ package com.android.documentsui;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
import android.database.AbstractCursor;
import android.database.Cursor;
@@ -62,10 +64,9 @@ public class SortingCursorWrapper extends AbstractCursor {
switch (sortOrder) {
case SORT_ORDER_DISPLAY_NAME:
- final String mimeType = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
- final String displayName = cursor.getString(
- cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME));
+ final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ final String displayName = getCursorString(
+ cursor, Document.COLUMN_DISPLAY_NAME);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
mValueString[i] = '\001' + displayName;
} else {
@@ -73,11 +74,10 @@ public class SortingCursorWrapper extends AbstractCursor {
}
break;
case SORT_ORDER_LAST_MODIFIED:
- mValueLong[i] = cursor.getLong(
- cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
+ mValueLong[i] = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
break;
case SORT_ORDER_SIZE:
- mValueLong[i] = cursor.getLong(cursor.getColumnIndex(Document.COLUMN_SIZE));
+ mValueLong[i] = getCursorLong(cursor, Document.COLUMN_SIZE);
break;
}
}