1e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay/*
2e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * Copyright (C) 2015 The Android Open Source Project
3e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay *
4e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * Licensed under the Apache License, Version 2.0 (the "License");
5e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * you may not use this file except in compliance with the License.
6e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * You may obtain a copy of the License at
7e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay *
8e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay *      http://www.apache.org/licenses/LICENSE-2.0
9e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay *
10e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * Unless required by applicable law or agreed to in writing, software
11e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * distributed under the License is distributed on an "AS IS" BASIS,
12e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * See the License for the specific language governing permissions and
14e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay * limitations under the License.
15e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay */
16e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
17e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKaypackage com.android.documentsui;
18e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
19a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKayimport static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
2083df8c072ddd01a5adc11da0869103c4645e6d76Steve McKayimport static com.android.documentsui.Shared.DEBUG;
21d0d9afc5782fdadcdbb52e91da77188929dbedbdSteve McKay
22e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.app.Activity;
23e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.app.FragmentManager;
24e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.content.ActivityNotFoundException;
25e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.content.ClipData;
26e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.content.ContentResolver;
27e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.content.ContentValues;
28e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.content.Intent;
29e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.net.Uri;
30e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.os.Bundle;
31323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKayimport android.os.Parcelable;
32323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKayimport android.provider.DocumentsContract;
3394b486d32c10d5ffbd8627977ca4759c246ecf4bBen Kwaimport android.support.design.widget.Snackbar;
34e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.util.Log;
353da8afcf30337656601e9fc57f0823b4afd17afaSteve McKayimport android.view.KeyEvent;
36e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.view.Menu;
37e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport android.view.MenuItem;
38e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
39a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKayimport com.android.documentsui.OperationDialogFragment.DialogType;
40e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport com.android.documentsui.RecentsProvider.ResumeColumns;
41ce71082254b6015ff6475374bc4d0001608b7636Steve McKayimport com.android.documentsui.dirlist.AnimationView;
42f862155e3476f8d9644d57072a6d92375bde628eSteve McKayimport com.android.documentsui.dirlist.DirectoryFragment;
433d988a969f39ca34ef89c449a0675164499c9a3aTomasz Mikolajewskiimport com.android.documentsui.dirlist.Model;
44e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport com.android.documentsui.model.DocumentInfo;
45e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport com.android.documentsui.model.DocumentStack;
46e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport com.android.documentsui.model.DurableUtils;
47e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport com.android.documentsui.model.RootInfo;
4814e827a958f713d9cdec550e0b1099622cda9479Steve McKayimport com.android.documentsui.services.FileOperationService;
49e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
50a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewskiimport java.io.FileNotFoundException;
5161686593fd32babb6e86754ea5bfa6bc95cd3690Tomasz Mikolajewskiimport java.util.ArrayList;
52e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport java.util.Arrays;
53a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewskiimport java.util.Collection;
54e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKayimport java.util.List;
55e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
56ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay/**
57273103b192d66375d560f98f3f099e8ff21d0607Steve McKay * Standalone file management activity.
58ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay */
5912055474a7f7119d3b44955e09358b9675167716Steve McKaypublic class FilesActivity extends BaseActivity {
60c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay
610f7078f0045f354ba6573f0094e097a9afd6534dBen Kwa    public static final String TAG = "FilesActivity";
62e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
63c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // See comments where this const is referenced for details.
64c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
65c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay
66c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // Track the time we opened the drawer in response to back being pressed.
67c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // We use the time gap to figure out whether to close app or reopen the drawer.
68c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    private long mDrawerLastFiddled;
69bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay    private DocumentClipper mClipper;
70ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay
7112055474a7f7119d3b44955e09358b9675167716Steve McKay    public FilesActivity() {
7212055474a7f7119d3b44955e09358b9675167716Steve McKay        super(R.layout.files_activity, TAG);
73ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay    }
74e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
75e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
76e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    public void onCreate(Bundle icicle) {
77e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        super.onCreate(icicle);
78e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
7912055474a7f7119d3b44955e09358b9675167716Steve McKay        mClipper = new DocumentClipper(this);
80bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay
81e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        RootsFragment.show(getFragmentManager(), null);
8283df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay
83aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        final Intent intent = getIntent();
84aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        final Uri uri = intent.getData();
85aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski
86323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        if (mState.restored) {
87aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski            if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
88aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        } else if (!mState.stack.isEmpty()) {
893c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // If a non-empty stack is present in our state, it was read (presumably)
90323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay            // from EXTRA_STACK intent extra. In this case, we'll skip other means of
913c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // loading or restoring the stack (like URI).
92aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski            //
933c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // When restoring from a stack, if a URI is present, it should only ever be:
943c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // -- a launch URI: Launch URIs support sensible activity management,
953c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            //    but don't specify a real content target)
963c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // -- a fake Uri from notifications. These URIs have no authority (TODO: details).
973c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            //
983c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // Any other URI is *sorta* unexpected...except when browsing an archive
993c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            // in downloads.
1003c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            if(uri != null
1013c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                    && uri.getAuthority() != null
1023c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                    && !uri.equals(mState.stack.peek())
1033c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                    && !LauncherActivity.isLaunchUri(uri)) {
1043c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                if (DEBUG) Log.w(TAG,
1053c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                        "Launching with non-empty stack. Ignoring unexpected uri: " + uri);
1063c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            } else {
1073c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay                if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
1083c6d499858d42cf3007ceaf029068cc7cdc05b8dSteve McKay            }
109ce71082254b6015ff6475374bc4d0001608b7636Steve McKay            refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
1108fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
111a1f7680f535a30aa816d129c072870031c8a2eb6Steve McKay            assert(uri != null);
11295cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay            new OpenUriForViewTask(this).executeOnExecutor(
113a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski                    ProviderExecutor.forAuthority(uri.getAuthority()), uri);
114aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        } else if (DocumentsContract.isRootUri(this, uri)) {
115aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski            if (DEBUG) Log.d(TAG, "Launching with root URI.");
116aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski            // If we've got a specific root to display, restore that root using a dedicated
117aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski            // authority. That way a misbehaving provider won't result in an ANR.
118c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            loadRoot(uri);
119aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        } else {
1206ce903d7b777ff5b668122a65d10da1d7d04373dSteve McKay            if (DEBUG) Log.d(TAG, "All other means skipped. Launching into default directory.");
1214e627169041376b25fc5d9d62de0eccfa61b2913Aga Wronska            loadRoot(getDefaultRoot());
122aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        }
123aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski
124748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski        final @DialogType int dialogType = intent.getIntExtra(
125748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski                FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
126aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        // DialogFragment takes care of restoring the dialog on configuration change.
127aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski        // Only show it manually for the first time (icicle is null).
128748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski        if (icicle == null && dialogType != DIALOG_TYPE_UNKNOWN) {
129748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski            final int opType = intent.getIntExtra(
130748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski                    FileOperationService.EXTRA_OPERATION,
131748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski                    FileOperationService.OPERATION_COPY);
132748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski            final ArrayList<DocumentInfo> srcList =
13314e827a958f713d9cdec550e0b1099622cda9479Steve McKay                    intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
134748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski            OperationDialogFragment.show(
135aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski                    getFragmentManager(),
136748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski                    dialogType,
137748ea8cc785b6f037518703308ffd3eb2a151c5aTomasz Mikolajewski                    srcList,
138aa684454f8e55fdba7829c7177799c0e8237d208Tomasz Mikolajewski                    mState.stack,
13914e827a958f713d9cdec550e0b1099622cda9479Steve McKay                    opType);
140e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        }
141e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
142e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
14312055474a7f7119d3b44955e09358b9675167716Steve McKay    @Override
14495cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    void includeState(State state) {
145e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final Intent intent = getIntent();
1460f7078f0045f354ba6573f0094e097a9afd6534dBen Kwa
14783df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        state.action = State.ACTION_BROWSE;
1480f7078f0045f354ba6573f0094e097a9afd6534dBen Kwa        state.allowMultiple = true;
149273103b192d66375d560f98f3f099e8ff21d0607Steve McKay
15083df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        // Options specific to the DocumentsActivity.
151a1f7680f535a30aa816d129c072870031c8a2eb6Steve McKay        assert(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
152273103b192d66375d560f98f3f099e8ff21d0607Steve McKay
153323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
15483df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        if (stack != null) {
155ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay            state.stack = stack;
15683df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        }
157e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
158e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
159e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
160e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    protected void onPostCreate(Bundle savedInstanceState) {
161e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        super.onPostCreate(savedInstanceState);
162b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // This check avoids a flicker from "Recents" to "Home".
163b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // Only update action bar at this point if there is an active
164b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // serach. Why? Because this avoid an early (undesired) load of
165b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // the recents root...which is the default root in other activities.
166b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // In Files app "Home" is the default, but it is loaded async.
1671f264a8a1a6c3e770e45c52fc08dc06de6b1aa55Steve McKay        // update will be called once Home root is loaded.
168b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // Except while searching we need this call to ensure the
169b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        // search bits get layed out correctly.
170b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        if (mSearchManager.isSearching()) {
1711f264a8a1a6c3e770e45c52fc08dc06de6b1aa55Steve McKay            mNavigator.update();
172b67bfbf975a453a67300f24fac6e4687a676a378Steve McKay        }
173e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
174e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
175e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
17683df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay    public void onResume() {
17783df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        super.onResume();
17883df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay
17983df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        final RootInfo root = getCurrentRoot();
18083df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay
18183df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        // If we're browsing a specific root, and that root went away, then we
18283df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        // have no reason to hang around.
18383df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        // TODO: Rather than just disappearing, maybe we should inform
18483df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        // the user what has happened, let them close us. Less surprising.
18583df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
18683df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay            finish();
18783df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay        }
18883df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay    }
18983df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay
19083df8c072ddd01a5adc11da0869103c4645e6d76Steve McKay    @Override
1911f264a8a1a6c3e770e45c52fc08dc06de6b1aa55Steve McKay    public String getDrawerTitle() {
19253f109b39e2b62fa3730ce1c43344df2c733251cSteve McKay        Intent intent = getIntent();
19353f109b39e2b62fa3730ce1c43344df2c733251cSteve McKay        return (intent != null && intent.hasExtra(Intent.EXTRA_TITLE))
19453f109b39e2b62fa3730ce1c43344df2c733251cSteve McKay                ? intent.getStringExtra(Intent.EXTRA_TITLE)
195ab58b5a3ddb7f0c477380e6bd91982e28a220d12Ben Lin                : getString(R.string.downloads_label);
196e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
197e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
198e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
199e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    public boolean onPrepareOptionsMenu(Menu menu) {
2005bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        super.onPrepareOptionsMenu(menu);
2013ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay
202efa1761776160376278fa467ea31d8e3f621a286Steve McKay        final RootInfo root = getCurrentRoot();
2039f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay
204e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
2059f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay        final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
206efa1761776160376278fa467ea31d8e3f621a286Steve McKay        final MenuItem settings = menu.findItem(R.id.menu_settings);
207f873769fba261fb26446b5082fddc4ca2724d931Steve McKay        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
208e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
2095bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        createDir.setVisible(true);
2105bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        createDir.setEnabled(canCreateDirectory());
2115bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
212efa1761776160376278fa467ea31d8e3f621a286Steve McKay        settings.setVisible(root.hasSettings());
213f570c771f23792837fb63f708bfa5c948d5fcad7Steve McKay        newWindow.setVisible(Shared.shouldShowFancyFeatures(this));
2149f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay
2155bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        Menus.disableHiddenItems(menu, pasteFromCb);
216b09985639fd116a133cb0ec27abc6975b11b2eb6Aga Wronska        // It hides icon if searching in progress
217b09985639fd116a133cb0ec27abc6975b11b2eb6Aga Wronska        mSearchManager.updateMenu();
2185bbae10c4c603ec6597f8dc7e1c7d060877cefc9Steve McKay        return true;
219e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
220e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
221e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
222bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay    public boolean onOptionsItemSelected(MenuItem item) {
2239f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay        switch (item.getItemId()) {
2249f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay            case R.id.menu_create_dir:
225a1f7680f535a30aa816d129c072870031c8a2eb6Steve McKay                assert(canCreateDirectory());
2269f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay                showCreateDirectoryDialog();
227cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska                break;
2289f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay            case R.id.menu_new_window:
229323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay                createNewWindow();
230cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska                break;
2319f9d5b432d6b87fe28a9f90f540b229d0f363538Steve McKay            case R.id.menu_paste_from_clipboard:
2323ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                DirectoryFragment dir = getDirectoryFragment();
2333ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                if (dir != null) {
2343ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                    dir.pasteFromClipboard();
2353ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                }
236cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska                break;
237cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska            default:
238cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska                return super.onOptionsItemSelected(item);
239bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay        }
240cf966ae5b5647e1a24bfb0220ed0dea5edd87ae8Aga Wronska        return true;
241bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay    }
242bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay
243323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    private void createNewWindow() {
24443df577ac46e36f21da3797d6f31a5ae6fa58c9fAga Wronska        Metrics.logUserAction(this, Metrics.USER_ACTION_NEW_WINDOW);
24543df577ac46e36f21da3797d6f31a5ae6fa58c9fAga Wronska
246323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        Intent intent = LauncherActivity.createLaunchIntent(this);
247323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
248a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay
249a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay        // With new multi-window mode we have to pick how we are launched.
250a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay        // By default we'd be launched in-place above the existing app.
251a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay        // By setting launch-to-side ActivityManager will open us to side.
252933076d80561751618f462b26309ce9e4c3ff3bfAndrii Kulian        if (isInMultiWindowMode()) {
2532a25a62739bca2f93aa91fbba698a6de697032b2Wale Ogunwale            intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
254a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay        }
255a7e923c57c30b72671191c924b9b4ccc9e9fcc40Steve McKay
256323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay        startActivity(intent);
257323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay    }
258323ee3ec094b78b9f96a8ab55b5c3b291028eeafSteve McKay
259bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay    @Override
260f6a31d319f3e08d26ca4c0fbfb5803056bafb243Aga Wronska    void refreshDirectory(int anim) {
261e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final FragmentManager fm = getFragmentManager();
262e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final RootInfo root = getCurrentRoot();
263e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final DocumentInfo cwd = getCurrentDirectory();
264e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
265a1f7680f535a30aa816d129c072870031c8a2eb6Steve McKay        assert(!mSearchManager.isSearching());
266893390bd580eca39ecd693cb0d76c7bc9b36a11dAga Wronska
267e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        if (cwd == null) {
268e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            DirectoryFragment.showRecentsOpen(fm, anim);
269e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        } else {
270893390bd580eca39ecd693cb0d76c7bc9b36a11dAga Wronska            // Normal boring directory
271893390bd580eca39ecd693cb0d76c7bc9b36a11dAga Wronska            DirectoryFragment.showDirectory(fm, root, cwd, anim);
272e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        }
273e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
274e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
275e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
2764f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay    void onRootPicked(RootInfo root) {
2774f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay        super.onRootPicked(root);
2784f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay        mDrawer.setOpen(false);
2794f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay    }
2804f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay
2814f1604088f11f6c5583d1148c34bc6a45e5c619fSteve McKay    @Override
2826eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay    public void onDocumentsPicked(List<DocumentInfo> docs) {
2836eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay        throw new UnsupportedOperationException();
2846eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay    }
2856eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay
2866eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay    @Override
2873d988a969f39ca34ef89c449a0675164499c9a3aTomasz Mikolajewski    public void onDocumentPicked(DocumentInfo doc, Model model) {
2883e63e7de2378055857c329e80862a11d947852c4Steve McKay        // Anything on downloads goes through the back through downloads manager
2893e63e7de2378055857c329e80862a11d947852c4Steve McKay        // (that's the MANAGE_DOCUMENT bit).
2903e63e7de2378055857c329e80862a11d947852c4Steve McKay        // This is done for two reasons:
2913e63e7de2378055857c329e80862a11d947852c4Steve McKay        // 1) The file in question might be a failed/queued or otherwise have some
2923e63e7de2378055857c329e80862a11d947852c4Steve McKay        //    specialized download handling.
2933e63e7de2378055857c329e80862a11d947852c4Steve McKay        // 2) For APKs, the download manager will add on some important security stuff
2943e63e7de2378055857c329e80862a11d947852c4Steve McKay        //    like origin URL.
2953e63e7de2378055857c329e80862a11d947852c4Steve McKay        // All other files not on downloads, event APKs, would get no benefit from this
2963e63e7de2378055857c329e80862a11d947852c4Steve McKay        // treatment, thusly the "isDownloads" check.
2978fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski
2988fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        // Launch MANAGE_DOCUMENTS only for the root level files, so it's not called for
2998fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        // files in archives. Also, if the activity is already browsing a ZIP from downloads,
3008fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        // then skip MANAGE_DOCUMENTS.
3018fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        final boolean isViewing = Intent.ACTION_VIEW.equals(getIntent().getAction());
3028fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        final boolean isInArchive = mState.stack.size() > 1;
3038fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        if (getCurrentRoot().isDownloads() && !isInArchive && !isViewing) {
3049bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            // First try managing the document; we expect manager to filter
3059bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            // based on authority, so we don't grant.
3069bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
3079bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            manage.setData(doc.derivedUri);
3089bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay
3099bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            try {
3109bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay                startActivity(manage);
3119bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay                return;
3129bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            } catch (ActivityNotFoundException ex) {
3139bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay                // fall back to regular handling below.
3149bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay            }
3159bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay        }
3169bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay
3178fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        if (doc.isContainer()) {
3188fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski            openContainerDocument(doc);
3198fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        } else {
3208fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski            openDocument(doc, model);
3218fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski        }
3228fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski    }
3238fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski
3248fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski    /**
3258fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski     * Launches an intent to view the specified document.
3268fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski     */
3278fa99701491dee0ac18b8707e32252cb3c621af1Tomasz Mikolajewski    private void openDocument(DocumentInfo doc, Model model) {
3283d988a969f39ca34ef89c449a0675164499c9a3aTomasz Mikolajewski        Intent intent = new QuickViewIntentBuilder(
3293d988a969f39ca34ef89c449a0675164499c9a3aTomasz Mikolajewski                getPackageManager(), getResources(), doc, model).build();
3306eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay
3313b2ad11fca93f07ab552ea41452e43168888833bSteve McKay        if (intent != null) {
3323b2ad11fca93f07ab552ea41452e43168888833bSteve McKay            // TODO: un-work around issue b/24963914. Should be fixed soon.
3333b2ad11fca93f07ab552ea41452e43168888833bSteve McKay            try {
3343b2ad11fca93f07ab552ea41452e43168888833bSteve McKay                startActivity(intent);
3353b2ad11fca93f07ab552ea41452e43168888833bSteve McKay                return;
3363b2ad11fca93f07ab552ea41452e43168888833bSteve McKay            } catch (SecurityException e) {
3371eafb666c24736be4454c199a62c256c55e86bc4Steve McKay                // Carry on to regular view mode.
3383b2ad11fca93f07ab552ea41452e43168888833bSteve McKay                Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
3393b2ad11fca93f07ab552ea41452e43168888833bSteve McKay            }
340e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        }
341c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay
3429bb98f3121e7efa2b232613f89bef99a80d07269Steve McKay        // Fall back to traditional VIEW action...
3433b2ad11fca93f07ab552ea41452e43168888833bSteve McKay        intent = new Intent(Intent.ACTION_VIEW);
3443f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        intent.setDataAndType(doc.derivedUri, doc.mimeType);
3453f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay
3463f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        // Downloads has traditionally added the WRITE permission
3473f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        // in the TrampolineActivity. Since this behavior is long
3483f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        // established, we set the same permission for non-managed files
3493f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        // This ensures consistent behavior between the Downloads root
3503f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        // and other roots.
3513f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
3523f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        if (doc.isWriteSupported()) {
3533f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay            flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
3543f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        }
3553f5885918b60824d98386f9c31b7dd189b7f426cSteve McKay        intent.setFlags(flags);
3563b2ad11fca93f07ab552ea41452e43168888833bSteve McKay
3576eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay        if (DEBUG && intent.getClipData() != null) {
3586eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay            Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData());
3596eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay        }
3606eaf3863c81779233c032b7f7fc5b90e94f5f70aSteve McKay
361c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay        try {
362c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay            startActivity(intent);
3633b2ad11fca93f07ab552ea41452e43168888833bSteve McKay        } catch (ActivityNotFoundException e) {
3643b2ad11fca93f07ab552ea41452e43168888833bSteve McKay            Snackbars.makeSnackbar(
3653b2ad11fca93f07ab552ea41452e43168888833bSteve McKay                    this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
366c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay        }
367c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay    }
368c78bcb8c7fa5db1726541c2c8bf8d4edfa0f1229Steve McKay
369e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    @Override
3703da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
3713da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay        DirectoryFragment dir;
3723ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay        // TODO: All key events should be statically bound using alphabeticShortcut.
3733ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay        // But not working.
3743da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay        switch (keyCode) {
3753da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay            case KeyEvent.KEYCODE_A:
3763ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                dir = getDirectoryFragment();
3773ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                if (dir != null) {
3783ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                    dir.selectAllFiles();
3793ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                }
3803da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay                return true;
381bdbd0ffdf2d5ee039c7a8ac58962276206467cb5Steve McKay            case KeyEvent.KEYCODE_C:
3823ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                dir = getDirectoryFragment();
3833ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                if (dir != null) {
3843ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                    dir.copySelectedToClipboard();
3853ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                }
3868fd086a18d9c71590a66a86d91b7f7471621a08fSteve McKay                return true;
3878fd086a18d9c71590a66a86d91b7f7471621a08fSteve McKay            case KeyEvent.KEYCODE_V:
3883ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                dir = getDirectoryFragment();
3893ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                if (dir != null) {
3903ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                    dir.pasteFromClipboard();
3913ce95951bd173c422b8d369b4651c28e5bcc20f4Steve McKay                }
3928fd086a18d9c71590a66a86d91b7f7471621a08fSteve McKay                return true;
3938fd086a18d9c71590a66a86d91b7f7471621a08fSteve McKay            default:
3948fd086a18d9c71590a66a86d91b7f7471621a08fSteve McKay                return super.onKeyShortcut(keyCode, event);
3953da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay        }
3963da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay    }
3973da8afcf30337656601e9fc57f0823b4afd17afaSteve McKay
398c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // Do some "do what a I want" drawer fiddling, but don't
399c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // do it if user already hit back recently and we recently
400c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    // did some fiddling.
401c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    @Override
402c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    boolean onBeforePopDir() {
403c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay        int size = mState.stack.size();
404c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay
405c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay        if (mDrawer.isPresent()
406c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                && (System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) {
407c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            // Close drawer if it is open.
408c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            if (mDrawer.isOpen()) {
409c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                mDrawer.setOpen(false);
410c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                mDrawerLastFiddled = System.currentTimeMillis();
411c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                return true;
412c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            }
413c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay
414c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            // Open the Close drawer if it is closed and we're at the top of a root.
41596e30eba1eaed0e35d6b8cfcd504aed5475556d8Aga Wronska            if (size <= 1) {
416c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                mDrawer.setOpen(true);
417c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                // Remember so we don't just close it again if back is pressed again.
418c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                mDrawerLastFiddled = System.currentTimeMillis();
419c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay                return true;
420c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay            }
421c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay        }
422c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay
423c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay        return false;
424c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay    }
425c95d87c98360e4254c55272c79f0f9a05215aaf6Steve McKay
42695cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    // Turns out only DocumentsActivity was ever calling saveStackBlocking.
42795cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    // There may be a  case where we want to contribute entries from
42895cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    // Behavior here in FilesActivity, but it isn't yet obvious.
42995cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    // TODO: Contribute to recents, or remove this.
43095cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    void writeStackToRecentsBlocking() {
431e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final ContentResolver resolver = getContentResolver();
432e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final ContentValues values = new ContentValues();
433e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
43495cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
435e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
436e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        // Remember location for next app launch
437e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final String packageName = getCallingPackageMaybeExtra();
438e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        values.clear();
439e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        values.put(ResumeColumns.STACK, rawStack);
440e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        values.put(ResumeColumns.EXTERNAL, 0);
441e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        resolver.insert(RecentsProvider.buildResume(packageName), values);
442e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
443e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
444ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay    @Override
445ef3e2cf35fbede340d5ff830b410baea8e461a94Steve McKay    void onTaskFinished(Uri... uris) {
446ffa829f779d7513c64bba76cc884c62b702f52ddBen Kwa        if (DEBUG) Log.d(TAG, "onFinished() " + Arrays.toString(uris));
447e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
448e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        final Intent intent = new Intent();
449e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        if (uris.length == 1) {
450e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            intent.setData(uris[0]);
451e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        } else if (uris.length > 1) {
452e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            final ClipData clipData = new ClipData(
453e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay                    null, mState.acceptMimes, new ClipData.Item(uris[0]));
454e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            for (int i = 1; i < uris.length; i++) {
455e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay                clipData.addItem(new ClipData.Item(uris[i]));
456e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            }
457e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay            intent.setClipData(clipData);
458e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        }
459e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
460e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
461e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
462e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
463e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay
464e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        setResult(Activity.RESULT_OK, intent);
465e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay        finish();
466e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay    }
467a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski
468a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski    /**
469a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski     * Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
470a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski     * to know which root to select. Also, the stack doesn't contain intermediate directories.
471a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski     * It's primarly used for opening ZIP archives from Downloads app.
472a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski     */
47395cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay    private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> {
47495cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay
47595cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        private final State mState;
47695cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        public OpenUriForViewTask(FilesActivity activity) {
47795cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay            super(activity);
47895cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay            mState = activity.mState;
47995cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        }
48095cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay
481a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski        @Override
48295cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        protected Void run(Uri... params) {
483a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            final Uri uri = params[0];
484a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski
48595cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay            final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
486a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            final String authority = uri.getAuthority();
487a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski
488a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            final Collection<RootInfo> roots =
489a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski                    rootsCache.getRootsForAuthorityBlocking(authority);
490a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            if (roots.isEmpty()) {
491a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski                Log.e(TAG, "Failed to find root for the requested Uri: " + uri);
492a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski                return null;
493a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            }
494a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski
495a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            final RootInfo root = roots.iterator().next();
496a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            mState.stack.root = root;
497a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            try {
49895cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay                mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
499a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            } catch (FileNotFoundException e) {
500a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski                Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
501a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            }
50295cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay            mState.stack.add(mOwner.getRootDocumentBlocking(root));
503a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski            return null;
504a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski        }
505a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski
506a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski        @Override
50795cd85ad3859991c756c8ff9bb683c97b3546eb9Steve McKay        protected void finish(Void result) {
508ce71082254b6015ff6475374bc4d0001608b7636Steve McKay            mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
509a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski        }
510a6120da1e37a4457bac58f8900bffa795dc8e7cbTomasz Mikolajewski    }
511e934ce6f8430f50d1d22bccd3b341b982f5541eeSteve McKay}
512