Merge "Simplify State initialization w/ better semantics + improved Task security."
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
protected DocumentInfo run(Void... params) {
|
||||
return mOwner.getRootDocumentBlocking(mRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(DocumentInfo result) {
|
||||
if (result != null && !isDestroyed()) {
|
||||
openContainerDocument(result);
|
||||
protected void finish(DocumentInfo result) {
|
||||
if (result != null) {
|
||||
mOwner.openContainerDocument(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (root != null) {
|
||||
onRootPicked(root);
|
||||
} else {
|
||||
Log.w(mTag, "Failed to find root: " + mRootUri);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> {
|
||||
private static final class HandleRootsChangedTask
|
||||
extends PairedTask<BaseActivity, RootInfo, RootInfo> {
|
||||
DocumentInfo mHome;
|
||||
|
||||
public HandleRootsChangedTask(BaseActivity activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
||||
@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()) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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)) {
|
||||
@@ -134,8 +139,6 @@ public class DocumentsActivity extends BaseActivity {
|
||||
state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION,
|
||||
FileOperationService.OPERATION_COPY);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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));
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.DocumentsContract;
|
||||
@@ -98,19 +97,19 @@ public class FilesActivity extends BaseActivity {
|
||||
refreshCurrentRootAndDirectory(ANIM_NONE);
|
||||
} else if (intent.getAction() == Intent.ACTION_VIEW) {
|
||||
checkArgument(uri != null);
|
||||
new OpenUriForViewTask().executeOnExecutor(
|
||||
new OpenUriForViewTask(this).executeOnExecutor(
|
||||
ProviderExecutor.forAuthority(uri.getAuthority()), uri);
|
||||
} else if (DocumentsContract.isRootUri(this, uri)) {
|
||||
if (DEBUG) Log.d(TAG, "Launching with root URI.");
|
||||
// If we've got a specific root to display, restore that root using a dedicated
|
||||
// authority. That way a misbehaving provider won't result in an ANR.
|
||||
new RestoreRootTask(uri).executeOnExecutor(
|
||||
new RestoreRootTask(this, uri).executeOnExecutor(
|
||||
ProviderExecutor.forAuthority(uri.getAuthority()));
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Launching into Home directory.");
|
||||
// If all else fails, try to load "Home" directory.
|
||||
final Uri homeUri = DocumentsContract.buildHomeUri();
|
||||
new RestoreRootTask(homeUri).executeOnExecutor(
|
||||
new RestoreRootTask(this, homeUri).executeOnExecutor(
|
||||
ProviderExecutor.forAuthority(homeUri.getAuthority()));
|
||||
}
|
||||
|
||||
@@ -134,9 +133,7 @@ public class FilesActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
State buildState() {
|
||||
State state = buildDefaultState();
|
||||
|
||||
void includeState(State state) {
|
||||
final Intent intent = getIntent();
|
||||
|
||||
state.action = State.ACTION_BROWSE;
|
||||
@@ -149,8 +146,6 @@ public class FilesActivity extends BaseActivity {
|
||||
if (stack != null) {
|
||||
state.stack = stack;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -363,13 +358,15 @@ public class FilesActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveStackBlocking() {
|
||||
// Turns out only DocumentsActivity was ever calling saveStackBlocking.
|
||||
// There may be a case where we want to contribute entries from
|
||||
// Behavior here in FilesActivity, but it isn't yet obvious.
|
||||
// TODO: Contribute to recents, or remove this.
|
||||
void writeStackToRecentsBlocking() {
|
||||
final ContentResolver resolver = getContentResolver();
|
||||
final ContentValues values = new ContentValues();
|
||||
|
||||
final byte[] rawStack = DurableUtils.writeToArrayOrNull(
|
||||
getDisplayState().stack);
|
||||
final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
|
||||
|
||||
// Remember location for next app launch
|
||||
final String packageName = getCallingPackageMaybeExtra();
|
||||
@@ -408,12 +405,19 @@ public class FilesActivity extends BaseActivity {
|
||||
* to know which root to select. Also, the stack doesn't contain intermediate directories.
|
||||
* It's primarly used for opening ZIP archives from Downloads app.
|
||||
*/
|
||||
final class OpenUriForViewTask extends AsyncTask<Uri, Void, Void> {
|
||||
private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> {
|
||||
|
||||
private final State mState;
|
||||
public OpenUriForViewTask(FilesActivity activity) {
|
||||
super(activity);
|
||||
mState = activity.mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Uri... params) {
|
||||
protected Void run(Uri... params) {
|
||||
final Uri uri = params[0];
|
||||
|
||||
final RootsCache rootsCache = DocumentsApplication.getRootsCache(FilesActivity.this);
|
||||
final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
|
||||
final String authority = uri.getAuthority();
|
||||
|
||||
final Collection<RootInfo> roots =
|
||||
@@ -426,20 +430,17 @@ public class FilesActivity extends BaseActivity {
|
||||
final RootInfo root = roots.iterator().next();
|
||||
mState.stack.root = root;
|
||||
try {
|
||||
mState.stack.add(DocumentInfo.fromUri(getContentResolver(), uri));
|
||||
mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
|
||||
}
|
||||
mState.stack.add(getRootDocumentBlocking(root));
|
||||
mState.stack.add(mOwner.getRootDocumentBlocking(root));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
if (isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
refreshCurrentRootAndDirectory(ANIM_NONE);
|
||||
protected void finish(Void result) {
|
||||
mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.documentsui.model.RootInfo;
|
||||
|
||||
final class RestoreRootTask extends PairedTask<BaseActivity, Void, RootInfo> {
|
||||
private static final String TAG = "RestoreRootTask";
|
||||
|
||||
private final Uri mRootUri;
|
||||
|
||||
public RestoreRootTask(BaseActivity activity, Uri rootUri) {
|
||||
super(activity);
|
||||
mRootUri = rootUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RootInfo run(Void... params) {
|
||||
String rootId = DocumentsContract.getRootId(mRootUri);
|
||||
return mOwner.mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish(RootInfo root) {
|
||||
mOwner.mState.restored = true;
|
||||
|
||||
if (root != null) {
|
||||
mOwner.onRootPicked(root);
|
||||
} else {
|
||||
Log.w(TAG, "Failed to find root: " + mRootUri);
|
||||
mOwner.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user