Loading packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +41 −110 Original line number Diff line number Diff line Loading @@ -45,22 +45,16 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.Spinner; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.SearchManager.SearchManagerListener; import com.android.documentsui.State.ViewMode; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; Loading @@ -86,8 +80,8 @@ public abstract class BaseActivity extends Activity abstract void onTaskFinished(Uri... uris); abstract void refreshDirectory(int anim); abstract void saveStackBlocking(); abstract State buildState(); /** Allows sub-classes to include information in a newly created State instance. */ abstract void includeState(State initialState); public BaseActivity(@LayoutRes int layoutId, String tag) { mLayoutId = layoutId; Loading @@ -102,9 +96,7 @@ public abstract class BaseActivity extends Activity setContentView(mLayoutId); mDrawer = DrawerController.create(this); mState = (icicle != null) ? icicle.<State>getParcelable(EXTRA_STATE) : buildState(); mState = getState(icicle); Metrics.logActivityLaunch(this, mState, getIntent()); mRoots = DocumentsApplication.getRootsCache(this); Loading @@ -113,7 +105,8 @@ public abstract class BaseActivity extends Activity new RootsCache.OnCacheUpdateListener() { @Override public void onCacheUpdate() { new HandleRootsChangedTask().execute(getCurrentRoot()); new HandleRootsChangedTask(BaseActivity.this) .execute(getCurrentRoot()); } }); Loading Loading @@ -182,7 +175,20 @@ public abstract class BaseActivity extends Activity super.onDestroy(); } State buildDefaultState() { private State getState(@Nullable Bundle icicle) { if (icicle != null) { State state = icicle.<State>getParcelable(EXTRA_STATE); if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state); return state; } State state = createSharedState(); includeState(state); if (DEBUG) Log.d(mTag, "Created new state object: " + state); return state; } private State createSharedState() { State state = new State(); final Intent intent = getIntent(); Loading Loading @@ -222,7 +228,7 @@ public abstract class BaseActivity extends Activity if (mRoots.isRecentsRoot(root)) { refreshCurrentRootAndDirectory(ANIM_NONE); } else { new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory()); new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory()); } } Loading Loading @@ -570,115 +576,40 @@ public abstract class BaseActivity extends Activity } } final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> { private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> { private RootInfo mRoot; public PickRootTask(RootInfo root) { public PickRootTask(BaseActivity activity, RootInfo root) { super(activity); mRoot = root; } @Override protected DocumentInfo doInBackground(Void... params) { return getRootDocumentBlocking(mRoot); } @Override protected void onPostExecute(DocumentInfo result) { if (result != null && !isDestroyed()) { openContainerDocument(result); } protected DocumentInfo run(Void... params) { return mOwner.getRootDocumentBlocking(mRoot); } } final class RestoreStackTask extends AsyncTask<Void, Void, Void> { private volatile boolean mRestoredStack; private volatile boolean mExternal; @Override protected Void doInBackground(Void... params) { if (DEBUG && !mState.stack.isEmpty()) { Log.w(mTag, "Overwriting existing stack."); } RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this); // Restore last stack for calling package final String packageName = getCallingPackageMaybeExtra(); final Cursor cursor = getContentResolver() .query(RecentsProvider.buildResume(packageName), null, null, null, null); try { if (cursor.moveToFirst()) { mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); mRestoredStack = true; } } catch (IOException e) { Log.w(mTag, "Failed to resume: " + e); } finally { IoUtils.closeQuietly(cursor); protected void finish(DocumentInfo result) { if (result != null) { mOwner.openContainerDocument(result); } if (mRestoredStack) { // Update the restored stack to ensure we have freshest data final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); try { mState.stack.updateRoot(matchingRoots); mState.stack.updateDocuments(getContentResolver()); } catch (FileNotFoundException e) { Log.w(mTag, "Failed to restore stack: " + e); mState.stack.reset(); mRestoredStack = false; } } return null; } @Override protected void onPostExecute(Void result) { if (isDestroyed()) return; mState.restored = true; refreshCurrentRootAndDirectory(ANIM_NONE); onStackRestored(mRestoredStack, mExternal); } } final 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; private static final class HandleRootsChangedTask extends PairedTask<BaseActivity, RootInfo, RootInfo> { DocumentInfo mHome; if (root != null) { onRootPicked(root); } else { Log.w(mTag, "Failed to find root: " + mRootUri); finish(); } } public HandleRootsChangedTask(BaseActivity activity) { super(activity); } final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> { DocumentInfo mHome; @Override protected RootInfo doInBackground(RootInfo... roots) { protected RootInfo run(RootInfo... roots) { checkArgument(roots.length == 1); final RootInfo currentRoot = roots[0]; final Collection<RootInfo> cachedRoots = mRoots.getRootsBlocking(); final Collection<RootInfo> cachedRoots = mOwner.mRoots.getRootsBlocking(); RootInfo homeRoot = null; for (final RootInfo root : cachedRoots) { if (root.isHome()) { Loading @@ -690,17 +621,17 @@ public abstract class BaseActivity extends Activity } } Preconditions.checkNotNull(homeRoot); mHome = getRootDocumentBlocking(homeRoot); mHome = mOwner.getRootDocumentBlocking(homeRoot); return homeRoot; } @Override protected void onPostExecute(RootInfo homeRoot) { if (homeRoot != null && mHome != null && !isDestroyed()) { protected void finish(RootInfo homeRoot) { if (homeRoot != null && mHome != null) { // Clear entire backstack and start in new root mState.onRootChanged(homeRoot); mSearchManager.update(homeRoot); openContainerDocument(mHome); mOwner.mState.onRootChanged(homeRoot); mOwner.mSearchManager.update(homeRoot); mOwner.openContainerDocument(mHome); } } } Loading packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +113 −38 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.documentsui; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.State.ACTION_CREATE; import static com.android.documentsui.State.ACTION_GET_CONTENT; import static com.android.documentsui.State.ACTION_OPEN; Loading @@ -31,11 +32,12 @@ import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; Loading @@ -52,7 +54,12 @@ import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; import libcore.io.IoUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.List; public class DocumentsActivity extends BaseActivity { Loading Loading @@ -94,16 +101,14 @@ public class DocumentsActivity extends BaseActivity { // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent // talkback from reading aloud the default title, we clear it here. setTitle(""); new RestoreStackTask().execute(); new RestoreStackTask(this).execute(); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override State buildState() { State state = buildDefaultState(); void includeState(State state) { final Intent intent = getIntent(); final String action = intent.getAction(); if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) { Loading Loading @@ -134,8 +139,6 @@ public class DocumentsActivity extends BaseActivity { state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_COPY); } return state; } @Override Loading Loading @@ -319,11 +322,13 @@ public class DocumentsActivity extends BaseActivity { } void onSaveRequested(DocumentInfo replaceTarget) { new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, replaceTarget.derivedUri) .executeOnExecutor(getExecutorForCurrentDirectory()); } void onSaveRequested(String mimeType, String displayName) { new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory()); new CreateFinishTask(this, mimeType, displayName) .executeOnExecutor(getExecutorForCurrentDirectory()); } @Override Loading @@ -343,7 +348,8 @@ public class DocumentsActivity extends BaseActivity { openContainerDocument(doc); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) { // Explicit file picked, return new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, doc.derivedUri) .executeOnExecutor(getExecutorForCurrentDirectory()); } else if (mState.action == ACTION_CREATE) { // Replace selected file SaveFragment.get(fm).setReplaceTarget(doc); Loading @@ -358,7 +364,8 @@ public class DocumentsActivity extends BaseActivity { for (int i = 0; i < size; i++) { uris[i] = docs.get(i).derivedUri; } new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, uris) .executeOnExecutor(getExecutorForCurrentDirectory()); } } Loading @@ -373,11 +380,10 @@ public class DocumentsActivity extends BaseActivity { // Should not be reached. throw new IllegalStateException("Invalid mState.action."); } new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory()); new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory()); } @Override void saveStackBlocking() { void writeStackToRecentsBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); Loading Loading @@ -438,69 +444,138 @@ public class DocumentsActivity extends BaseActivity { finish(); } public static DocumentsActivity get(Fragment fragment) { return (DocumentsActivity) fragment.getActivity(); } private final class PickFinishTask extends AsyncTask<Void, Void, Void> { /** * Restores the stack from Recents for the specified package. */ private static final class RestoreStackTask extends PairedTask<DocumentsActivity, Void, Void> { private volatile boolean mRestoredStack; private volatile boolean mExternal; private Context mContext; private State mState; public RestoreStackTask(DocumentsActivity activity) { super(activity); mState = activity.mState; } @Override protected Void run(Void... params) { if (DEBUG && !mState.stack.isEmpty()) { Log.w(TAG, "Overwriting existing stack."); } RootsCache roots = DocumentsApplication.getRootsCache(mContext); String packageName = mOwner.getCallingPackageMaybeExtra(); Uri resumeUri = RecentsProvider.buildResume(packageName); Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null); try { if (cursor.moveToFirst()) { mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); mRestoredStack = true; } } catch (IOException e) { Log.w(TAG, "Failed to resume: " + e); } finally { IoUtils.closeQuietly(cursor); } if (mRestoredStack) { // Update the restored stack to ensure we have freshest data final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); try { mState.stack.updateRoot(matchingRoots); mState.stack.updateDocuments(mOwner.getContentResolver()); } catch (FileNotFoundException e) { Log.w(TAG, "Failed to restore stack for package: " + packageName + " because of error: "+ e); mState.stack.reset(); mRestoredStack = false; } } return null; } @Override protected void finish(Void result) { mState.restored = true; mOwner.refreshCurrentRootAndDirectory(ANIM_NONE); mOwner.onStackRestored(mRestoredStack, mExternal); } } private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri mUri; public PickFinishTask(Uri uri) { public PickFinishTask(DocumentsActivity activity, Uri uri) { super(activity); mUri = uri; } @Override protected Void doInBackground(Void... params) { saveStackBlocking(); protected Void run(Void... params) { mOwner.writeStackToRecentsBlocking(); return null; } @Override protected void onPostExecute(Void result) { onTaskFinished(mUri); protected void finish(Void result) { mOwner.onTaskFinished(mUri); } } final class ExistingFinishTask extends AsyncTask<Void, Void, Void> { private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri[] mUris; public ExistingFinishTask(Uri... uris) { public ExistingFinishTask(DocumentsActivity activity, Uri... uris) { super(activity); mUris = uris; } @Override protected Void doInBackground(Void... params) { saveStackBlocking(); protected Void run(Void... params) { mOwner.writeStackToRecentsBlocking(); return null; } @Override protected void onPostExecute(Void result) { onTaskFinished(mUris); protected void finish(Void result) { mOwner.onTaskFinished(mUris); } } /** * Task that creates a new document in the background. */ final class CreateFinishTask extends AsyncTask<Void, Void, Uri> { private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> { private final String mMimeType; private final String mDisplayName; public CreateFinishTask(String mimeType, String displayName) { public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) { super(activity); mMimeType = mimeType; mDisplayName = displayName; } @Override protected void onPreExecute() { setPending(true); protected void prepare() { mOwner.setPending(true); } @Override protected Uri doInBackground(Void... params) { final ContentResolver resolver = getContentResolver(); final DocumentInfo cwd = getCurrentDirectory(); protected Uri run(Void... params) { final ContentResolver resolver = mOwner.getContentResolver(); final DocumentInfo cwd = mOwner.getCurrentDirectory(); ContentProviderClient client = null; Uri childUri = null; Loading @@ -516,22 +591,22 @@ public class DocumentsActivity extends BaseActivity { } if (childUri != null) { saveStackBlocking(); mOwner.writeStackToRecentsBlocking(); } return childUri; } @Override protected void onPostExecute(Uri result) { protected void finish(Uri result) { if (result != null) { onTaskFinished(result); mOwner.onTaskFinished(result); } else { Snackbars.makeSnackbar( DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show(); mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show(); } setPending(false); mOwner.setPending(false); } } } packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java +2 −25 Original line number Diff line number Diff line Loading @@ -24,8 +24,6 @@ import android.app.Fragment; import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; Loading @@ -37,10 +35,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; Loading Loading @@ -72,23 +68,19 @@ public class DownloadsActivity extends BaseActivity { // talkback from reading aloud the default title, we clear it here. setTitle(""); final Uri rootUri = getIntent().getData(); new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); new RestoreRootTask(this, rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override State buildState() { State state = buildDefaultState(); void includeState(State state) { state.action = ACTION_MANAGE; state.acceptMimes = new String[] { "*/*" }; state.allowMultiple = true; state.showSize = true; state.excludedAuthorities = getExcludedAuthorities(); return state; } @Override Loading Loading @@ -169,21 +161,6 @@ public class DownloadsActivity extends BaseActivity { @Override public void onDocumentsPicked(List<DocumentInfo> docs) {} @Override void saveStackBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); // Remember location for next app launch final String packageName = getCallingPackageMaybeExtra(); values.clear(); values.put(ResumeColumns.STACK, rawStack); values.put(ResumeColumns.EXTERNAL, 0); resolver.insert(RecentsProvider.buildResume(packageName), values); } @Override void onTaskFinished(Uri... uris) { Log.d(TAG, "onFinished() " + Arrays.toString(uris)); Loading packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +24 −23 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/DocumentsUI/src/com/android/documentsui/PairedTask.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.Activity; import android.os.AsyncTask; /** * An {@link AsyncTask} that guards work with checks that a paired {@link Activity} * is still alive. Instances of this class make no progress. * * <p>Use this type of task for greater safety when executing tasks that might complete * after an Activity is destroyed. * * <p>Also useful as tasks can be static, limiting scope, but still have access to * the owning class (by way the A template and the mActivity field). * * @template Owner Activity type. * @template Input input type * @template Output output type */ abstract class PairedTask<Owner extends Activity, Input, Output> extends AsyncTask<Input, Void, Output> { protected final Owner mOwner; public PairedTask(Owner owner) { mOwner = owner; } /** Called prior to run being executed. Analogous to {@link AsyncTask#onPreExecute} */ void prepare() {} /** Analogous to {@link AsyncTask#doInBackground} */ abstract Output run(Input... input); /** Analogous to {@link AsyncTask#onPostExecute} */ abstract void finish(Output output); @Override final protected void onPreExecute() { if (mOwner.isDestroyed()) { return; } prepare(); } @Override final protected Output doInBackground(Input... input) { if (mOwner.isDestroyed()) { return null; } return run(input); } @Override final protected void onPostExecute(Output result) { if (mOwner.isDestroyed()) { return; } finish(result); } } Loading
packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +41 −110 Original line number Diff line number Diff line Loading @@ -45,22 +45,16 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.Spinner; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.SearchManager.SearchManagerListener; import com.android.documentsui.State.ViewMode; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; Loading @@ -86,8 +80,8 @@ public abstract class BaseActivity extends Activity abstract void onTaskFinished(Uri... uris); abstract void refreshDirectory(int anim); abstract void saveStackBlocking(); abstract State buildState(); /** Allows sub-classes to include information in a newly created State instance. */ abstract void includeState(State initialState); public BaseActivity(@LayoutRes int layoutId, String tag) { mLayoutId = layoutId; Loading @@ -102,9 +96,7 @@ public abstract class BaseActivity extends Activity setContentView(mLayoutId); mDrawer = DrawerController.create(this); mState = (icicle != null) ? icicle.<State>getParcelable(EXTRA_STATE) : buildState(); mState = getState(icicle); Metrics.logActivityLaunch(this, mState, getIntent()); mRoots = DocumentsApplication.getRootsCache(this); Loading @@ -113,7 +105,8 @@ public abstract class BaseActivity extends Activity new RootsCache.OnCacheUpdateListener() { @Override public void onCacheUpdate() { new HandleRootsChangedTask().execute(getCurrentRoot()); new HandleRootsChangedTask(BaseActivity.this) .execute(getCurrentRoot()); } }); Loading Loading @@ -182,7 +175,20 @@ public abstract class BaseActivity extends Activity super.onDestroy(); } State buildDefaultState() { private State getState(@Nullable Bundle icicle) { if (icicle != null) { State state = icicle.<State>getParcelable(EXTRA_STATE); if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state); return state; } State state = createSharedState(); includeState(state); if (DEBUG) Log.d(mTag, "Created new state object: " + state); return state; } private State createSharedState() { State state = new State(); final Intent intent = getIntent(); Loading Loading @@ -222,7 +228,7 @@ public abstract class BaseActivity extends Activity if (mRoots.isRecentsRoot(root)) { refreshCurrentRootAndDirectory(ANIM_NONE); } else { new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory()); new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory()); } } Loading Loading @@ -570,115 +576,40 @@ public abstract class BaseActivity extends Activity } } final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> { private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> { private RootInfo mRoot; public PickRootTask(RootInfo root) { public PickRootTask(BaseActivity activity, RootInfo root) { super(activity); mRoot = root; } @Override protected DocumentInfo doInBackground(Void... params) { return getRootDocumentBlocking(mRoot); } @Override protected void onPostExecute(DocumentInfo result) { if (result != null && !isDestroyed()) { openContainerDocument(result); } protected DocumentInfo run(Void... params) { return mOwner.getRootDocumentBlocking(mRoot); } } final class RestoreStackTask extends AsyncTask<Void, Void, Void> { private volatile boolean mRestoredStack; private volatile boolean mExternal; @Override protected Void doInBackground(Void... params) { if (DEBUG && !mState.stack.isEmpty()) { Log.w(mTag, "Overwriting existing stack."); } RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this); // Restore last stack for calling package final String packageName = getCallingPackageMaybeExtra(); final Cursor cursor = getContentResolver() .query(RecentsProvider.buildResume(packageName), null, null, null, null); try { if (cursor.moveToFirst()) { mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); mRestoredStack = true; } } catch (IOException e) { Log.w(mTag, "Failed to resume: " + e); } finally { IoUtils.closeQuietly(cursor); protected void finish(DocumentInfo result) { if (result != null) { mOwner.openContainerDocument(result); } if (mRestoredStack) { // Update the restored stack to ensure we have freshest data final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); try { mState.stack.updateRoot(matchingRoots); mState.stack.updateDocuments(getContentResolver()); } catch (FileNotFoundException e) { Log.w(mTag, "Failed to restore stack: " + e); mState.stack.reset(); mRestoredStack = false; } } return null; } @Override protected void onPostExecute(Void result) { if (isDestroyed()) return; mState.restored = true; refreshCurrentRootAndDirectory(ANIM_NONE); onStackRestored(mRestoredStack, mExternal); } } final 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; private static final class HandleRootsChangedTask extends PairedTask<BaseActivity, RootInfo, RootInfo> { DocumentInfo mHome; if (root != null) { onRootPicked(root); } else { Log.w(mTag, "Failed to find root: " + mRootUri); finish(); } } public HandleRootsChangedTask(BaseActivity activity) { super(activity); } final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> { DocumentInfo mHome; @Override protected RootInfo doInBackground(RootInfo... roots) { protected RootInfo run(RootInfo... roots) { checkArgument(roots.length == 1); final RootInfo currentRoot = roots[0]; final Collection<RootInfo> cachedRoots = mRoots.getRootsBlocking(); final Collection<RootInfo> cachedRoots = mOwner.mRoots.getRootsBlocking(); RootInfo homeRoot = null; for (final RootInfo root : cachedRoots) { if (root.isHome()) { Loading @@ -690,17 +621,17 @@ public abstract class BaseActivity extends Activity } } Preconditions.checkNotNull(homeRoot); mHome = getRootDocumentBlocking(homeRoot); mHome = mOwner.getRootDocumentBlocking(homeRoot); return homeRoot; } @Override protected void onPostExecute(RootInfo homeRoot) { if (homeRoot != null && mHome != null && !isDestroyed()) { protected void finish(RootInfo homeRoot) { if (homeRoot != null && mHome != null) { // Clear entire backstack and start in new root mState.onRootChanged(homeRoot); mSearchManager.update(homeRoot); openContainerDocument(mHome); mOwner.mState.onRootChanged(homeRoot); mOwner.mSearchManager.update(homeRoot); mOwner.openContainerDocument(mHome); } } } Loading
packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +113 −38 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.documentsui; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.State.ACTION_CREATE; import static com.android.documentsui.State.ACTION_GET_CONTENT; import static com.android.documentsui.State.ACTION_OPEN; Loading @@ -31,11 +32,12 @@ import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.provider.DocumentsContract; Loading @@ -52,7 +54,12 @@ import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; import libcore.io.IoUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.List; public class DocumentsActivity extends BaseActivity { Loading Loading @@ -94,16 +101,14 @@ public class DocumentsActivity extends BaseActivity { // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent // talkback from reading aloud the default title, we clear it here. setTitle(""); new RestoreStackTask().execute(); new RestoreStackTask(this).execute(); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override State buildState() { State state = buildDefaultState(); void includeState(State state) { final Intent intent = getIntent(); final String action = intent.getAction(); if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) { Loading Loading @@ -134,8 +139,6 @@ public class DocumentsActivity extends BaseActivity { state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_COPY); } return state; } @Override Loading Loading @@ -319,11 +322,13 @@ public class DocumentsActivity extends BaseActivity { } void onSaveRequested(DocumentInfo replaceTarget) { new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, replaceTarget.derivedUri) .executeOnExecutor(getExecutorForCurrentDirectory()); } void onSaveRequested(String mimeType, String displayName) { new CreateFinishTask(mimeType, displayName).executeOnExecutor(getExecutorForCurrentDirectory()); new CreateFinishTask(this, mimeType, displayName) .executeOnExecutor(getExecutorForCurrentDirectory()); } @Override Loading @@ -343,7 +348,8 @@ public class DocumentsActivity extends BaseActivity { openContainerDocument(doc); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) { // Explicit file picked, return new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, doc.derivedUri) .executeOnExecutor(getExecutorForCurrentDirectory()); } else if (mState.action == ACTION_CREATE) { // Replace selected file SaveFragment.get(fm).setReplaceTarget(doc); Loading @@ -358,7 +364,8 @@ public class DocumentsActivity extends BaseActivity { for (int i = 0; i < size; i++) { uris[i] = docs.get(i).derivedUri; } new ExistingFinishTask(uris).executeOnExecutor(getExecutorForCurrentDirectory()); new ExistingFinishTask(this, uris) .executeOnExecutor(getExecutorForCurrentDirectory()); } } Loading @@ -373,11 +380,10 @@ public class DocumentsActivity extends BaseActivity { // Should not be reached. throw new IllegalStateException("Invalid mState.action."); } new PickFinishTask(result).executeOnExecutor(getExecutorForCurrentDirectory()); new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory()); } @Override void saveStackBlocking() { void writeStackToRecentsBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); Loading Loading @@ -438,69 +444,138 @@ public class DocumentsActivity extends BaseActivity { finish(); } public static DocumentsActivity get(Fragment fragment) { return (DocumentsActivity) fragment.getActivity(); } private final class PickFinishTask extends AsyncTask<Void, Void, Void> { /** * Restores the stack from Recents for the specified package. */ private static final class RestoreStackTask extends PairedTask<DocumentsActivity, Void, Void> { private volatile boolean mRestoredStack; private volatile boolean mExternal; private Context mContext; private State mState; public RestoreStackTask(DocumentsActivity activity) { super(activity); mState = activity.mState; } @Override protected Void run(Void... params) { if (DEBUG && !mState.stack.isEmpty()) { Log.w(TAG, "Overwriting existing stack."); } RootsCache roots = DocumentsApplication.getRootsCache(mContext); String packageName = mOwner.getCallingPackageMaybeExtra(); Uri resumeUri = RecentsProvider.buildResume(packageName); Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null); try { if (cursor.moveToFirst()) { mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0; final byte[] rawStack = cursor.getBlob( cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, mState.stack); mRestoredStack = true; } } catch (IOException e) { Log.w(TAG, "Failed to resume: " + e); } finally { IoUtils.closeQuietly(cursor); } if (mRestoredStack) { // Update the restored stack to ensure we have freshest data final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState); try { mState.stack.updateRoot(matchingRoots); mState.stack.updateDocuments(mOwner.getContentResolver()); } catch (FileNotFoundException e) { Log.w(TAG, "Failed to restore stack for package: " + packageName + " because of error: "+ e); mState.stack.reset(); mRestoredStack = false; } } return null; } @Override protected void finish(Void result) { mState.restored = true; mOwner.refreshCurrentRootAndDirectory(ANIM_NONE); mOwner.onStackRestored(mRestoredStack, mExternal); } } private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri mUri; public PickFinishTask(Uri uri) { public PickFinishTask(DocumentsActivity activity, Uri uri) { super(activity); mUri = uri; } @Override protected Void doInBackground(Void... params) { saveStackBlocking(); protected Void run(Void... params) { mOwner.writeStackToRecentsBlocking(); return null; } @Override protected void onPostExecute(Void result) { onTaskFinished(mUri); protected void finish(Void result) { mOwner.onTaskFinished(mUri); } } final class ExistingFinishTask extends AsyncTask<Void, Void, Void> { private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> { private final Uri[] mUris; public ExistingFinishTask(Uri... uris) { public ExistingFinishTask(DocumentsActivity activity, Uri... uris) { super(activity); mUris = uris; } @Override protected Void doInBackground(Void... params) { saveStackBlocking(); protected Void run(Void... params) { mOwner.writeStackToRecentsBlocking(); return null; } @Override protected void onPostExecute(Void result) { onTaskFinished(mUris); protected void finish(Void result) { mOwner.onTaskFinished(mUris); } } /** * Task that creates a new document in the background. */ final class CreateFinishTask extends AsyncTask<Void, Void, Uri> { private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> { private final String mMimeType; private final String mDisplayName; public CreateFinishTask(String mimeType, String displayName) { public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) { super(activity); mMimeType = mimeType; mDisplayName = displayName; } @Override protected void onPreExecute() { setPending(true); protected void prepare() { mOwner.setPending(true); } @Override protected Uri doInBackground(Void... params) { final ContentResolver resolver = getContentResolver(); final DocumentInfo cwd = getCurrentDirectory(); protected Uri run(Void... params) { final ContentResolver resolver = mOwner.getContentResolver(); final DocumentInfo cwd = mOwner.getCurrentDirectory(); ContentProviderClient client = null; Uri childUri = null; Loading @@ -516,22 +591,22 @@ public class DocumentsActivity extends BaseActivity { } if (childUri != null) { saveStackBlocking(); mOwner.writeStackToRecentsBlocking(); } return childUri; } @Override protected void onPostExecute(Uri result) { protected void finish(Uri result) { if (result != null) { onTaskFinished(result); mOwner.onTaskFinished(result); } else { Snackbars.makeSnackbar( DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show(); mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show(); } setPending(false); mOwner.setPending(false); } } }
packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java +2 −25 Original line number Diff line number Diff line Loading @@ -24,8 +24,6 @@ import android.app.Fragment; import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; Loading @@ -37,10 +35,8 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toolbar; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; Loading Loading @@ -72,23 +68,19 @@ public class DownloadsActivity extends BaseActivity { // talkback from reading aloud the default title, we clear it here. setTitle(""); final Uri rootUri = getIntent().getData(); new RestoreRootTask(rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); new RestoreRootTask(this, rootUri).executeOnExecutor(getExecutorForCurrentDirectory()); } else { refreshCurrentRootAndDirectory(ANIM_NONE); } } @Override State buildState() { State state = buildDefaultState(); void includeState(State state) { state.action = ACTION_MANAGE; state.acceptMimes = new String[] { "*/*" }; state.allowMultiple = true; state.showSize = true; state.excludedAuthorities = getExcludedAuthorities(); return state; } @Override Loading Loading @@ -169,21 +161,6 @@ public class DownloadsActivity extends BaseActivity { @Override public void onDocumentsPicked(List<DocumentInfo> docs) {} @Override void saveStackBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); // Remember location for next app launch final String packageName = getCallingPackageMaybeExtra(); values.clear(); values.put(ResumeColumns.STACK, rawStack); values.put(ResumeColumns.EXTERNAL, 0); resolver.insert(RecentsProvider.buildResume(packageName), values); } @Override void onTaskFinished(Uri... uris) { Log.d(TAG, "onFinished() " + Arrays.toString(uris)); Loading
packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java +24 −23 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/DocumentsUI/src/com/android/documentsui/PairedTask.java 0 → 100644 +77 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.Activity; import android.os.AsyncTask; /** * An {@link AsyncTask} that guards work with checks that a paired {@link Activity} * is still alive. Instances of this class make no progress. * * <p>Use this type of task for greater safety when executing tasks that might complete * after an Activity is destroyed. * * <p>Also useful as tasks can be static, limiting scope, but still have access to * the owning class (by way the A template and the mActivity field). * * @template Owner Activity type. * @template Input input type * @template Output output type */ abstract class PairedTask<Owner extends Activity, Input, Output> extends AsyncTask<Input, Void, Output> { protected final Owner mOwner; public PairedTask(Owner owner) { mOwner = owner; } /** Called prior to run being executed. Analogous to {@link AsyncTask#onPreExecute} */ void prepare() {} /** Analogous to {@link AsyncTask#doInBackground} */ abstract Output run(Input... input); /** Analogous to {@link AsyncTask#onPostExecute} */ abstract void finish(Output output); @Override final protected void onPreExecute() { if (mOwner.isDestroyed()) { return; } prepare(); } @Override final protected Output doInBackground(Input... input) { if (mOwner.isDestroyed()) { return null; } return run(input); } @Override final protected void onPostExecute(Output result) { if (mOwner.isDestroyed()) { return; } finish(result); } }