Merge "Provide previous stack when opening new window."
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index a6a45e5..ab0f666 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.app.Activity;
@@ -150,12 +151,10 @@
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
                 ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
 
-        State state = getDisplayState();
-
-        sortSize.setVisible(state.showSize); // Only sort by size when visible
-        fileSize.setVisible(!state.showSize);
-        grid.setVisible(state.derivedMode != State.MODE_GRID);
-        list.setVisible(state.derivedMode != State.MODE_LIST);
+        sortSize.setVisible(mState.showSize); // Only sort by size when visible
+        fileSize.setVisible(!mState.showSize);
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
         advanced.setVisible(!mState.showAdvanced);
         settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
@@ -185,12 +184,10 @@
     void onStackRestored(boolean restored, boolean external) {}
 
     void onRootPicked(RootInfo root) {
-        State state = getDisplayState();
-
         // Clear entire backstack and start in new root
-        state.stack.root = root;
-        state.stack.clear();
-        state.stackTouched = true;
+        mState.stack.root = root;
+        mState.stack.clear();
+        mState.stackTouched = true;
 
         mSearchManager.update(root);
 
@@ -289,8 +286,8 @@
     }
 
     void openDirectory(DocumentInfo doc) {
-        getDisplayState().stack.push(doc);
-        getDisplayState().stackTouched = true;
+        mState.stack.push(doc);
+        mState.stackTouched = true;
         onCurrentDirectoryChanged(ANIM_DOWN);
     }
 
@@ -367,16 +364,15 @@
     }
 
     void setDisplayAdvancedDevices(boolean display) {
-        State state = getDisplayState();
         LocalPreferences.setDisplayAdvancedDevices(this, display);
-        state.showAdvanced = state.forceAdvanced | display;
+        mState.showAdvanced = mState.forceAdvanced | display;
         RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
 
     void setDisplayFileSize(boolean display) {
         LocalPreferences.setDisplayFileSize(this, display);
-        getDisplayState().showSize = display;
+        mState.showSize = display;
         DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
@@ -389,7 +385,7 @@
      * Set state sort order based on explicit user action.
      */
     void setUserSortOrder(int sortOrder) {
-        getDisplayState().userSortOrder = sortOrder;
+        mState.userSortOrder = sortOrder;
         DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
     }
 
@@ -397,7 +393,7 @@
      * Set state mode based on explicit user action.
      */
     void setUserMode(int mode) {
-        getDisplayState().userMode = mode;
+        mState.userMode = mode;
         DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
     }
 
@@ -411,7 +407,7 @@
     @Override
     protected void onSaveInstanceState(Bundle state) {
         super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, getDisplayState());
+        state.putParcelable(EXTRA_STATE, mState);
     }
 
     @Override
@@ -420,16 +416,15 @@
     }
 
     RootInfo getCurrentRoot() {
-        State state = getDisplayState();
-        if (state.stack.root != null) {
-            return state.stack.root;
+        if (mState.stack.root != null) {
+            return mState.stack.root;
         } else {
             return mRoots.getRecentsRoot();
         }
     }
 
     public DocumentInfo getCurrentDirectory() {
-        return getDisplayState().stack.peek();
+        return mState.stack.peek();
     }
 
     public Executor getExecutorForCurrentDirectory() {
@@ -470,9 +465,8 @@
             // Update the restored stack to ensure we have freshest data
             stack.updateDocuments(getContentResolver());
 
-            State state = getDisplayState();
-            state.stack = stack;
-            state.stackTouched = true;
+            mState.stack = stack;
+            mState.stackTouched = true;
             onCurrentDirectoryChanged(ANIM_SIDE);
 
         } catch (FileNotFoundException e) {
@@ -502,9 +496,8 @@
         @Override
         protected void onPostExecute(DocumentInfo result) {
             if (result != null) {
-                State state = getDisplayState();
-                state.stack.push(result);
-                state.stackTouched = true;
+                mState.stack.push(result);
+                mState.stackTouched = true;
                 onCurrentDirectoryChanged(ANIM_SIDE);
             }
         }
@@ -516,7 +509,9 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            State state = getDisplayState();
+            if (DEBUG && !mState.stack.isEmpty()) {
+                Log.w(mTag, "Overwriting existing stack.");
+            }
             RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
 
             // Restore last stack for calling package
@@ -528,7 +523,7 @@
                     mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
                     final byte[] rawStack = cursor.getBlob(
                             cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, state.stack);
+                    DurableUtils.readFromArray(rawStack, mState.stack);
                     mRestoredStack = true;
                 }
             } catch (IOException e) {
@@ -539,13 +534,13 @@
 
             if (mRestoredStack) {
                 // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
                 try {
-                    state.stack.updateRoot(matchingRoots);
-                    state.stack.updateDocuments(getContentResolver());
+                    mState.stack.updateRoot(matchingRoots);
+                    mState.stack.updateDocuments(getContentResolver());
                 } catch (FileNotFoundException e) {
                     Log.w(mTag, "Failed to restore stack: " + e);
-                    state.stack.reset();
+                    mState.stack.reset();
                     mRestoredStack = false;
                 }
             }
@@ -556,7 +551,7 @@
         @Override
         protected void onPostExecute(Void result) {
             if (isDestroyed()) return;
-            getDisplayState().restored = true;
+            mState.restored = true;
             onCurrentDirectoryChanged(ANIM_NONE);
             onStackRestored(mRestoredStack, mExternal);
         }
@@ -600,10 +595,9 @@
                 return;
             }
 
-            State state = getDisplayState();
-            while (state.stack.size() > position + 1) {
-                state.stackTouched = true;
-                state.stack.pop();
+            while (mState.stack.size() > position + 1) {
+                mState.stackTouched = true;
+                mState.stack.pop();
             }
             onCurrentDirectoryChanged(ANIM_UP);
         }
@@ -620,13 +614,12 @@
     final class StackAdapter extends BaseAdapter {
         @Override
         public int getCount() {
-            return getDisplayState().stack.size();
+            return mState.stack.size();
         }
 
         @Override
         public DocumentInfo getItem(int position) {
-            State state = getDisplayState();
-            return state.stack.get(state.stack.size() - position - 1);
+            return mState.stack.get(mState.stack.size() - position - 1);
         }
 
         @Override
@@ -714,13 +707,12 @@
                 return;
             }
 
-            State state = getDisplayState();
-            if (state.currentSearch != null) {
+            if (mState.currentSearch != null) {
                 mMenu.expandActionView();
 
                 mView.setIconified(false);
                 mView.clearFocus();
-                mView.setQuery(state.currentSearch, false);
+                mView.setQuery(mState.currentSearch, false);
             } else {
                 mView.clearFocus();
                 if (!mView.isIconified()) {
@@ -746,7 +738,7 @@
 
             mMenu.setVisible(visible);
             if (!visible) {
-                getDisplayState().currentSearch = null;
+                mState.currentSearch = null;
             }
         }
 
@@ -764,7 +756,7 @@
         }
 
         boolean isSearching() {
-            return getDisplayState().currentSearch != null;
+            return mState.currentSearch != null;
         }
 
         boolean isExpanded() {
@@ -779,7 +771,7 @@
                 return false;
             }
 
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return false;
         }
@@ -798,7 +790,7 @@
                 mIgnoreNextCollapse = false;
                 return true;
             }
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
         }
@@ -806,7 +798,7 @@
         @Override
         public boolean onQueryTextSubmit(String query) {
             mSearchExpanded = true;
-            getDisplayState().currentSearch = query;
+            mState.currentSearch = query;
             mView.clearFocus();
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
diff --git a/src/com/android/documentsui/CopyService.java b/src/com/android/documentsui/CopyService.java
index 815fbfe..362052c 100644
--- a/src/com/android/documentsui/CopyService.java
+++ b/src/com/android/documentsui/CopyService.java
@@ -62,7 +62,6 @@
 
     private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
     public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
-    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
     public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
     public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
 
@@ -115,7 +114,7 @@
         final Intent copyIntent = new Intent(activity, CopyService.class);
         copyIntent.putParcelableArrayListExtra(
                 EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
-        copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
         copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
 
         int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin
@@ -142,7 +141,7 @@
         }
 
         final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
-        final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         // Copy by default.
         final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
 
@@ -173,7 +172,7 @@
                 Log.e(TAG, mFailedFiles.size() + " files failed to copy");
                 final Context context = getApplicationContext();
                 final Intent navigateIntent = new Intent(context, FilesActivity.class);
-                navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+                navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
                 navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
                 navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
                 navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
@@ -221,7 +220,7 @@
 
         final Context context = getApplicationContext();
         final Intent navigateIntent = new Intent(context, FilesActivity.class);
-        navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+        navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         final String contentTitle = getString(copying ? R.string.copy_notification_title
                 : R.string.move_notification_title);
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index 3c6be6e..b3ce103 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -423,7 +423,7 @@
         }
 
         CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
-                (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK),
+                (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
                 data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE));
     }
 
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index f6ded4b..ced03cc 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -510,7 +510,7 @@
         } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
             // Picking a copy destination is only used internally by us, so we
             // don't need to extend permissions to the caller.
-            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
+            intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
             intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
diff --git a/src/com/android/documentsui/FailureDialogFragment.java b/src/com/android/documentsui/FailureDialogFragment.java
index ea0c18a..120f610 100644
--- a/src/com/android/documentsui/FailureDialogFragment.java
+++ b/src/com/android/documentsui/FailureDialogFragment.java
@@ -66,7 +66,7 @@
         if (whichButton == DialogInterface.BUTTON_POSITIVE) {
             CopyService.start(getActivity(), mFailedSrcList,
                     (DocumentStack) getActivity().getIntent().getParcelableExtra(
-                            CopyService.EXTRA_STACK),
+                            Shared.EXTRA_STACK),
                             mTransferMode);
         }
     }
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index df803fb..f6a5131 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -30,6 +30,8 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.DocumentsContract;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.util.Log;
@@ -85,32 +87,49 @@
         mDrawer = DrawerController.create(this);
 
         RootsFragment.show(getFragmentManager(), null);
-        if (!mState.restored) {
-            Intent intent = getIntent();
-            Uri rootUri = intent.getData();
 
-            // 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.
-            if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
-                new RestoreRootTask(rootUri).executeOnExecutor(
-                        ProviderExecutor.forAuthority(rootUri.getAuthority()));
+        if (mState.restored) {
+            onCurrentDirectoryChanged(ANIM_NONE);
+        } else {
+            Intent intent = getIntent();
+            Uri uri = intent.getData();
+
+            // If a non-empty stack is present in our state it was read (presumably)
+            // from EXTRA_STACK intent extra. In this case, we'll skip other means of
+            // loading or restoring the stack.
+            if (!mState.stack.isEmpty()) {
+                // When restoring from a stack, if a URI is present, it should only ever
+                // be a launch URI. Launch URIs support sensible activity management, but
+                // don't specify an real content target.
+                if (uri != null) {
+                    checkState(LauncherActivity.isLaunchUri(uri));
+                }
+                onCurrentDirectoryChanged(ANIM_NONE);
+            } else if (DocumentsContract.isRootUri(this, 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(
+                        ProviderExecutor.forAuthority(uri.getAuthority()));
             } else {
+                // Finally, we try to restore a stack from recents.
                 new RestoreStackTask().execute();
             }
 
+            // TODO: Ensure we're handling CopyService errors correctly across all activities.
             // Show a failure dialog if there was a failed operation.
-            final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
             final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
             final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
                     CopyService.TRANSFER_MODE_NONE);
             if (failure != 0) {
                 final ArrayList<DocumentInfo> failedSrcList =
                         intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
-                FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack,
+                FailureDialogFragment.show(
+                        getFragmentManager(),
+                        failure,
+                        failedSrcList,
+                        mState.stack,
                         transferMode);
             }
-        } else {
-            onCurrentDirectoryChanged(ANIM_NONE);
         }
     }
 
@@ -126,7 +145,7 @@
         // Options specific to the DocumentsActivity.
         checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
 
-        final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         if (stack != null) {
             state.stack = stack;
         }
@@ -240,7 +259,7 @@
                 showCreateDirectoryDialog();
                 return true;
             case R.id.menu_new_window:
-                startActivity(LauncherActivity.createLaunchIntent(this));
+                createNewWindow();
                 return true;
             case R.id.menu_paste_from_clipboard:
                 DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
@@ -252,6 +271,12 @@
         return super.onOptionsItemSelected(item);
     }
 
+    private void createNewWindow() {
+        Intent intent = LauncherActivity.createLaunchIntent(this);
+        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
+        startActivity(intent);
+    }
+
     @Override
     void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
diff --git a/src/com/android/documentsui/Shared.java b/src/com/android/documentsui/Shared.java
index 29bcd24..e06d6a5 100644
--- a/src/com/android/documentsui/Shared.java
+++ b/src/com/android/documentsui/Shared.java
@@ -23,12 +23,11 @@
 import android.support.design.widget.Snackbar;
 import android.view.View;
 
-/**
- * @hide
- */
+/** @hide */
 public final class Shared {
     public static final boolean DEBUG = true;
     public static final String TAG = "Documents";
+    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
 
     /**
      * Generates a formatted quantity string.
diff --git a/tests/src/com/android/documentsui/CopyTest.java b/tests/src/com/android/documentsui/CopyTest.java
index 568e9e4..fc42c3b 100644
--- a/tests/src/com/android/documentsui/CopyTest.java
+++ b/tests/src/com/android/documentsui/CopyTest.java
@@ -425,7 +425,7 @@
         stack.push(DocumentInfo.fromUri(mResolver, dst));
         final Intent copyIntent = new Intent(mContext, CopyService.class);
         copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
-        copyIntent.putExtra(CopyService.EXTRA_STACK, (Parcelable) stack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         // startService(copyIntent);
         return copyIntent;