Loading packages/DocumentsUI/AndroidManifest.xml +0 −8 Original line number Diff line number Diff line Loading @@ -48,14 +48,6 @@ android:authorities="com.android.documentsui.recents" android:exported="false" /> <receiver android:name=".DocumentChangedReceiver"> <intent-filter> <action android:name="android.provider.action.DOCUMENT_CHANGED" /> <data android:mimeType="vnd.android.cursor.dir/root" /> <data android:mimeType="vnd.android.cursor.item/root" /> </intent-filter> </receiver> <!-- TODO: remove when we have real clients --> <activity android:name=".TestActivity" android:enabled="false"> <intent-filter> Loading packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +5 −4 Original line number Diff line number Diff line Loading @@ -249,8 +249,7 @@ public class DirectoryFragment extends Fragment { context, mType, root, doc, contentsUri, state.userSortOrder); case TYPE_RECENT_OPEN: final RootsCache roots = DocumentsApplication.getRootsCache(context); final List<RootInfo> matchingRoots = roots.getMatchingRoots(state); return new RecentLoader(context, matchingRoots, state.acceptMimes); return new RecentLoader(context, roots, state); default: throw new IllegalStateException("Unknown type " + mType); } Loading Loading @@ -797,7 +796,9 @@ public class DirectoryFragment extends Fragment { Drawable iconDrawable = null; if (mType == TYPE_RECENT_OPEN) { final RootInfo root = roots.getRoot(docAuthority, docRootId); // We've already had to enumerate roots before any results can // be shown, so this will never block. final RootInfo root = roots.getRootBlocking(docAuthority, docRootId); iconDrawable = root.loadIcon(context); if (summary != null) { Loading @@ -808,7 +809,7 @@ public class DirectoryFragment extends Fragment { summary.setVisibility(View.VISIBLE); hasLine2 = true; } else { if (iconDrawable != null && roots.isIconUnique(root)) { if (iconDrawable != null && roots.isIconUniqueBlocking(root)) { // No summary needed if icon speaks for itself summary.setVisibility(View.INVISIBLE); } else { Loading packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +56 −13 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -87,6 +88,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Collection; import java.util.List; public class DocumentsActivity extends Activity { Loading Loading @@ -207,8 +209,17 @@ public class DocumentsActivity extends Activity { RootsFragment.show(getFragmentManager(), null); } if (!mState.restored) { if (mState.action == ACTION_MANAGE) { final Uri rootUri = getIntent().getData(); new RestoreRootTask(rootUri).execute(); } else { new RestoreStackTask().execute(); } } else { onCurrentDirectoryChanged(ANIM_NONE); } } private void buildDefaultState() { mState = new State(); Loading Loading @@ -241,22 +252,41 @@ public class DocumentsActivity extends Activity { mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false); mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this); } private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> { private Uri mRootUri; public RestoreRootTask(Uri rootUri) { mRootUri = rootUri; } @Override protected RootInfo doInBackground(Void... params) { final String rootId = DocumentsContract.getRootId(mRootUri); return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId); } @Override protected void onPostExecute(RootInfo root) { if (isDestroyed()) return; mState.restored = true; if (mState.action == ACTION_MANAGE) { final Uri uri = intent.getData(); final String rootId = DocumentsContract.getRootId(uri); final RootInfo root = mRoots.getRoot(uri.getAuthority(), rootId); if (root != null) { onRootPicked(root, true); } else { Log.w(TAG, "Failed to find root: " + uri); Log.w(TAG, "Failed to find root: " + mRootUri); finish(); } } } } else { private class RestoreStackTask extends AsyncTask<Void, Void, Void> { private volatile boolean mRestoredStack; @Override protected Void doInBackground(Void... params) { // Restore last stack for calling package // TODO: move into async loader boolean restoredStack = false; final String packageName = getCallingPackage(); final Cursor cursor = getContentResolver() .query(RecentsProvider.buildResume(packageName), null, null, null, null); Loading @@ -265,7 +295,7 @@ public class DocumentsActivity extends Activity { final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); restoredStack = true; mRestoredStack = true; } } catch (IOException e) { Log.w(TAG, "Failed to resume", e); Loading @@ -275,18 +305,28 @@ public class DocumentsActivity extends Activity { // If restored root isn't valid, fall back to recents final RootInfo root = getCurrentRoot(); final List<RootInfo> matchingRoots = mRoots.getMatchingRoots(mState); final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState); if (!matchingRoots.contains(root)) { mState.stack.reset(); restoredStack = false; mRestoredStack = false; } return null; } @Override protected void onPostExecute(Void result) { if (isDestroyed()) return; mState.restored = true; // Only open drawer when not restoring stack, and when not showing // visual content. if (!restoredStack if (!mRestoredStack && !MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) { setRootsDrawerOpen(true); } onCurrentDirectoryChanged(ANIM_NONE); } } Loading Loading @@ -935,6 +975,7 @@ public class DocumentsActivity extends Activity { public boolean localOnly = false; public boolean showAdvanced = false; public boolean stackTouched = false; public boolean restored = false; /** Current user navigation stack; empty implies recents. */ public DocumentStack stack = new DocumentStack(); Loading Loading @@ -974,6 +1015,7 @@ public class DocumentsActivity extends Activity { out.writeInt(localOnly ? 1 : 0); out.writeInt(showAdvanced ? 1 : 0); out.writeInt(stackTouched ? 1 : 0); out.writeInt(restored ? 1 : 0); DurableUtils.writeToParcel(out, stack); out.writeString(currentSearch); out.writeMap(dirState); Loading @@ -992,6 +1034,7 @@ public class DocumentsActivity extends Activity { state.localOnly = in.readInt() != 0; state.showAdvanced = in.readInt() != 0; state.stackTouched = in.readInt() != 0; state.restored = in.readInt() != 0; DurableUtils.readFromParcel(in, state.stack); state.currentSearch = in.readString(); in.readMap(state.dirState, null); Loading packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java +10 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Point; import android.net.Uri; public class DocumentsApplication extends Application { private RootsCache mRoots; Loading @@ -49,6 +50,8 @@ public class DocumentsApplication extends Application { final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024; mRoots = new RootsCache(this); mRoots.updateAsync(); mThumbnails = new ThumbnailCache(memoryClassBytes / 4); final IntentFilter packageFilter = new IntentFilter(); Loading Loading @@ -77,8 +80,13 @@ public class DocumentsApplication extends Application { private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO: narrow changed/removed to only packages that have backends mRoots.update(); final Uri data = intent.getData(); if (data != null) { final String packageName = data.getSchemeSpecificPart(); mRoots.updatePackageAsync(packageName); } else { mRoots.updateAsync(); } } }; } packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +8 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; Loading Loading @@ -80,8 +81,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { return executor; } private final List<RootInfo> mRoots; private final String[] mAcceptMimes; private final RootsCache mRoots; private final State mState; private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap(); Loading Loading @@ -138,10 +139,10 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { } } public RecentLoader(Context context, List<RootInfo> roots, String[] acceptMimes) { public RecentLoader(Context context, RootsCache roots, State state) { super(context); mRoots = roots; mAcceptMimes = acceptMimes; mState = state; } @Override Loading @@ -150,7 +151,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { // First time through we kick off all the recent tasks, and wait // around to see if everyone finishes quickly. for (RootInfo root : mRoots) { final Collection<RootInfo> roots = mRoots.getMatchingRootsBlocking(mState); for (RootInfo root : roots) { if ((root.flags & Root.FLAG_SUPPORTS_RECENTS) != 0) { final RecentTask task = new RecentTask(root.authority, root.rootId); mTasks.put(root, task); Loading @@ -177,7 +179,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { try { final Cursor cursor = task.get(); final FilteringCursorWrapper filtered = new FilteringCursorWrapper( cursor, mAcceptMimes, new String[] { Document.MIME_TYPE_DIR }) { cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) { @Override public void close() { // Ignored, since we manage cursor lifecycle internally Loading Loading
packages/DocumentsUI/AndroidManifest.xml +0 −8 Original line number Diff line number Diff line Loading @@ -48,14 +48,6 @@ android:authorities="com.android.documentsui.recents" android:exported="false" /> <receiver android:name=".DocumentChangedReceiver"> <intent-filter> <action android:name="android.provider.action.DOCUMENT_CHANGED" /> <data android:mimeType="vnd.android.cursor.dir/root" /> <data android:mimeType="vnd.android.cursor.item/root" /> </intent-filter> </receiver> <!-- TODO: remove when we have real clients --> <activity android:name=".TestActivity" android:enabled="false"> <intent-filter> Loading
packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +5 −4 Original line number Diff line number Diff line Loading @@ -249,8 +249,7 @@ public class DirectoryFragment extends Fragment { context, mType, root, doc, contentsUri, state.userSortOrder); case TYPE_RECENT_OPEN: final RootsCache roots = DocumentsApplication.getRootsCache(context); final List<RootInfo> matchingRoots = roots.getMatchingRoots(state); return new RecentLoader(context, matchingRoots, state.acceptMimes); return new RecentLoader(context, roots, state); default: throw new IllegalStateException("Unknown type " + mType); } Loading Loading @@ -797,7 +796,9 @@ public class DirectoryFragment extends Fragment { Drawable iconDrawable = null; if (mType == TYPE_RECENT_OPEN) { final RootInfo root = roots.getRoot(docAuthority, docRootId); // We've already had to enumerate roots before any results can // be shown, so this will never block. final RootInfo root = roots.getRootBlocking(docAuthority, docRootId); iconDrawable = root.loadIcon(context); if (summary != null) { Loading @@ -808,7 +809,7 @@ public class DirectoryFragment extends Fragment { summary.setVisibility(View.VISIBLE); hasLine2 = true; } else { if (iconDrawable != null && roots.isIconUnique(root)) { if (iconDrawable != null && roots.isIconUniqueBlocking(root)) { // No summary needed if icon speaks for itself summary.setVisibility(View.INVISIBLE); } else { Loading
packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +56 −13 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; Loading Loading @@ -87,6 +88,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Collection; import java.util.List; public class DocumentsActivity extends Activity { Loading Loading @@ -207,8 +209,17 @@ public class DocumentsActivity extends Activity { RootsFragment.show(getFragmentManager(), null); } if (!mState.restored) { if (mState.action == ACTION_MANAGE) { final Uri rootUri = getIntent().getData(); new RestoreRootTask(rootUri).execute(); } else { new RestoreStackTask().execute(); } } else { onCurrentDirectoryChanged(ANIM_NONE); } } private void buildDefaultState() { mState = new State(); Loading Loading @@ -241,22 +252,41 @@ public class DocumentsActivity extends Activity { mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false); mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this); } private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> { private Uri mRootUri; public RestoreRootTask(Uri rootUri) { mRootUri = rootUri; } @Override protected RootInfo doInBackground(Void... params) { final String rootId = DocumentsContract.getRootId(mRootUri); return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId); } @Override protected void onPostExecute(RootInfo root) { if (isDestroyed()) return; mState.restored = true; if (mState.action == ACTION_MANAGE) { final Uri uri = intent.getData(); final String rootId = DocumentsContract.getRootId(uri); final RootInfo root = mRoots.getRoot(uri.getAuthority(), rootId); if (root != null) { onRootPicked(root, true); } else { Log.w(TAG, "Failed to find root: " + uri); Log.w(TAG, "Failed to find root: " + mRootUri); finish(); } } } } else { private class RestoreStackTask extends AsyncTask<Void, Void, Void> { private volatile boolean mRestoredStack; @Override protected Void doInBackground(Void... params) { // Restore last stack for calling package // TODO: move into async loader boolean restoredStack = false; final String packageName = getCallingPackage(); final Cursor cursor = getContentResolver() .query(RecentsProvider.buildResume(packageName), null, null, null, null); Loading @@ -265,7 +295,7 @@ public class DocumentsActivity extends Activity { final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); restoredStack = true; mRestoredStack = true; } } catch (IOException e) { Log.w(TAG, "Failed to resume", e); Loading @@ -275,18 +305,28 @@ public class DocumentsActivity extends Activity { // If restored root isn't valid, fall back to recents final RootInfo root = getCurrentRoot(); final List<RootInfo> matchingRoots = mRoots.getMatchingRoots(mState); final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState); if (!matchingRoots.contains(root)) { mState.stack.reset(); restoredStack = false; mRestoredStack = false; } return null; } @Override protected void onPostExecute(Void result) { if (isDestroyed()) return; mState.restored = true; // Only open drawer when not restoring stack, and when not showing // visual content. if (!restoredStack if (!mRestoredStack && !MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) { setRootsDrawerOpen(true); } onCurrentDirectoryChanged(ANIM_NONE); } } Loading Loading @@ -935,6 +975,7 @@ public class DocumentsActivity extends Activity { public boolean localOnly = false; public boolean showAdvanced = false; public boolean stackTouched = false; public boolean restored = false; /** Current user navigation stack; empty implies recents. */ public DocumentStack stack = new DocumentStack(); Loading Loading @@ -974,6 +1015,7 @@ public class DocumentsActivity extends Activity { out.writeInt(localOnly ? 1 : 0); out.writeInt(showAdvanced ? 1 : 0); out.writeInt(stackTouched ? 1 : 0); out.writeInt(restored ? 1 : 0); DurableUtils.writeToParcel(out, stack); out.writeString(currentSearch); out.writeMap(dirState); Loading @@ -992,6 +1034,7 @@ public class DocumentsActivity extends Activity { state.localOnly = in.readInt() != 0; state.showAdvanced = in.readInt() != 0; state.stackTouched = in.readInt() != 0; state.restored = in.readInt() != 0; DurableUtils.readFromParcel(in, state.stack); state.currentSearch = in.readString(); in.readMap(state.dirState, null); Loading
packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java +10 −2 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Point; import android.net.Uri; public class DocumentsApplication extends Application { private RootsCache mRoots; Loading @@ -49,6 +50,8 @@ public class DocumentsApplication extends Application { final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024; mRoots = new RootsCache(this); mRoots.updateAsync(); mThumbnails = new ThumbnailCache(memoryClassBytes / 4); final IntentFilter packageFilter = new IntentFilter(); Loading Loading @@ -77,8 +80,13 @@ public class DocumentsApplication extends Application { private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO: narrow changed/removed to only packages that have backends mRoots.update(); final Uri data = intent.getData(); if (data != null) { final String packageName = data.getSchemeSpecificPart(); mRoots.updatePackageAsync(packageName); } else { mRoots.updateAsync(); } } }; }
packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +8 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; Loading Loading @@ -80,8 +81,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { return executor; } private final List<RootInfo> mRoots; private final String[] mAcceptMimes; private final RootsCache mRoots; private final State mState; private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap(); Loading Loading @@ -138,10 +139,10 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { } } public RecentLoader(Context context, List<RootInfo> roots, String[] acceptMimes) { public RecentLoader(Context context, RootsCache roots, State state) { super(context); mRoots = roots; mAcceptMimes = acceptMimes; mState = state; } @Override Loading @@ -150,7 +151,8 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { // First time through we kick off all the recent tasks, and wait // around to see if everyone finishes quickly. for (RootInfo root : mRoots) { final Collection<RootInfo> roots = mRoots.getMatchingRootsBlocking(mState); for (RootInfo root : roots) { if ((root.flags & Root.FLAG_SUPPORTS_RECENTS) != 0) { final RecentTask task = new RecentTask(root.authority, root.rootId); mTasks.put(root, task); Loading @@ -177,7 +179,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { try { final Cursor cursor = task.get(); final FilteringCursorWrapper filtered = new FilteringCursorWrapper( cursor, mAcceptMimes, new String[] { Document.MIME_TYPE_DIR }) { cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) { @Override public void close() { // Ignored, since we manage cursor lifecycle internally Loading