blob: c17be067d55dccb91ef7056ecfb811896803a26a [file] [log] [blame]
Steve McKayd0a2a2c2015-03-25 14:35:33 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui;
18
Steve McKaydf5add42016-01-28 12:02:57 -080019import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
Steve McKay459bc2b2015-09-16 15:07:31 -070020import static com.android.documentsui.Shared.DEBUG;
Steve McKayf68210e2015-11-03 15:23:16 -080021import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
Steve McKay7ef09a22015-05-06 12:16:40 -070022
Steve McKayd0a2a2c2015-03-25 14:35:33 -070023import android.app.Activity;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070024import android.app.FragmentManager;
25import android.content.ActivityNotFoundException;
26import android.content.ClipData;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070027import android.content.ContentResolver;
28import android.content.ContentValues;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070029import android.content.Intent;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070030import android.net.Uri;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070031import android.os.Bundle;
Steve McKay4d0255f2015-09-25 16:02:56 -070032import android.os.Parcelable;
33import android.provider.DocumentsContract;
Ben Kwac4693342015-09-30 10:00:10 -070034import android.support.design.widget.Snackbar;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070035import android.util.Log;
Steve McKay0599a442015-05-05 14:50:00 -070036import android.view.KeyEvent;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070037import android.view.Menu;
38import android.view.MenuItem;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070039
Steve McKaydf5add42016-01-28 12:02:57 -080040import com.android.documentsui.OperationDialogFragment.DialogType;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070041import com.android.documentsui.RecentsProvider.ResumeColumns;
Steve McKayf68210e2015-11-03 15:23:16 -080042import com.android.documentsui.dirlist.DirectoryFragment;
Tomasz Mikolajewskid71bd612016-02-16 12:28:43 +090043import com.android.documentsui.dirlist.Model;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070044import com.android.documentsui.model.DocumentInfo;
45import com.android.documentsui.model.DocumentStack;
46import com.android.documentsui.model.DurableUtils;
47import com.android.documentsui.model.RootInfo;
Steve McKayc83baa02016-01-06 18:32:13 -080048import com.android.documentsui.services.FileOperationService;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070049
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +090050import java.io.FileNotFoundException;
Tomasz Mikolajewski332d8192015-04-13 19:38:43 +090051import java.util.ArrayList;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070052import java.util.Arrays;
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +090053import java.util.Collection;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070054import java.util.List;
Steve McKayd0a2a2c2015-03-25 14:35:33 -070055
Steve McKayb68dd222015-04-20 17:18:15 -070056/**
Steve McKay64ac2512015-05-12 12:49:58 -070057 * Standalone file management activity.
Steve McKayb68dd222015-04-20 17:18:15 -070058 */
Steve McKay0fbfc652015-08-20 16:48:49 -070059public class FilesActivity extends BaseActivity {
Steve McKayedc65bb2015-07-31 14:35:22 -070060
Ben Kwa0574b182015-09-08 07:31:19 -070061 public static final String TAG = "FilesActivity";
Steve McKayd0a2a2c2015-03-25 14:35:33 -070062
Steve McKayfd8425a2016-02-23 14:34:50 -080063 // See comments where this const is referenced for details.
64 private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
65
66 // Track the time we opened the drawer in response to back being pressed.
67 // We use the time gap to figure out whether to close app or reopen the drawer.
68 private long mDrawerLastFiddled;
Steve McKay1f199482015-05-20 15:58:42 -070069 private DocumentClipper mClipper;
Steve McKayb68dd222015-04-20 17:18:15 -070070
Steve McKay0fbfc652015-08-20 16:48:49 -070071 public FilesActivity() {
72 super(R.layout.files_activity, TAG);
Steve McKayb68dd222015-04-20 17:18:15 -070073 }
Steve McKayd0a2a2c2015-03-25 14:35:33 -070074
75 @Override
76 public void onCreate(Bundle icicle) {
Steve McKayd0a2a2c2015-03-25 14:35:33 -070077 super.onCreate(icicle);
78
Steve McKay0fbfc652015-08-20 16:48:49 -070079 mClipper = new DocumentClipper(this);
Steve McKay1f199482015-05-20 15:58:42 -070080
Steve McKayd0a2a2c2015-03-25 14:35:33 -070081 RootsFragment.show(getFragmentManager(), null);
Steve McKay459bc2b2015-09-16 15:07:31 -070082
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +090083 final Intent intent = getIntent();
84 final Uri uri = intent.getData();
Steve McKay4d0255f2015-09-25 16:02:56 -070085
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +090086 if (mState.restored) {
87 if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +090088 } else if (!mState.stack.isEmpty()) {
Steve McKay4d0255f2015-09-25 16:02:56 -070089 // If a non-empty stack is present in our state it was read (presumably)
90 // from EXTRA_STACK intent extra. In this case, we'll skip other means of
91 // loading or restoring the stack.
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +090092 //
93 // When restoring from a stack, if a URI is present, it should only ever
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +090094 // be a launch URI, or a fake Uri from notifications.
95 // Launch URIs support sensible activity management, but don't specify a real
96 // content target.
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +090097 if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
Steve McKay0af8afd2016-02-25 13:34:03 -080098 assert(uri == null || uri.getAuthority() == null ||
Tomasz Mikolajewski63e2aae2016-02-01 12:01:14 +090099 LauncherActivity.isLaunchUri(uri));
Aga Wronska8788dad2016-01-15 17:30:15 -0800100 refreshCurrentRootAndDirectory(ANIM_NONE);
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900101 } else if (intent.getAction() == Intent.ACTION_VIEW) {
Steve McKay0af8afd2016-02-25 13:34:03 -0800102 assert(uri != null);
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800103 new OpenUriForViewTask(this).executeOnExecutor(
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900104 ProviderExecutor.forAuthority(uri.getAuthority()), uri);
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900105 } else if (DocumentsContract.isRootUri(this, uri)) {
106 if (DEBUG) Log.d(TAG, "Launching with root URI.");
107 // If we've got a specific root to display, restore that root using a dedicated
108 // authority. That way a misbehaving provider won't result in an ANR.
Steve McKayfd8425a2016-02-23 14:34:50 -0800109 loadRoot(uri);
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900110 } else {
111 if (DEBUG) Log.d(TAG, "Launching into Home directory.");
112 // If all else fails, try to load "Home" directory.
113 final Uri homeUri = DocumentsContract.buildHomeUri();
Steve McKayfd8425a2016-02-23 14:34:50 -0800114 loadRoot(homeUri);
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900115 }
Tomasz Mikolajewski9452c442015-04-14 16:32:41 +0900116
Tomasz Mikolajewskidd2b31c2016-01-22 16:22:51 +0900117 final @DialogType int dialogType = intent.getIntExtra(
118 FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900119 // DialogFragment takes care of restoring the dialog on configuration change.
120 // Only show it manually for the first time (icicle is null).
Tomasz Mikolajewskidd2b31c2016-01-22 16:22:51 +0900121 if (icicle == null && dialogType != DIALOG_TYPE_UNKNOWN) {
122 final int opType = intent.getIntExtra(
123 FileOperationService.EXTRA_OPERATION,
124 FileOperationService.OPERATION_COPY);
125 final ArrayList<DocumentInfo> srcList =
Steve McKayc83baa02016-01-06 18:32:13 -0800126 intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
Tomasz Mikolajewskidd2b31c2016-01-22 16:22:51 +0900127 OperationDialogFragment.show(
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900128 getFragmentManager(),
Tomasz Mikolajewskidd2b31c2016-01-22 16:22:51 +0900129 dialogType,
130 srcList,
Tomasz Mikolajewskicdbbbe02015-12-25 17:06:52 +0900131 mState.stack,
Steve McKayc83baa02016-01-06 18:32:13 -0800132 opType);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700133 }
134 }
135
Steve McKay0fbfc652015-08-20 16:48:49 -0700136 @Override
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800137 void includeState(State state) {
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700138 final Intent intent = getIntent();
Ben Kwa0574b182015-09-08 07:31:19 -0700139
Steve McKay459bc2b2015-09-16 15:07:31 -0700140 state.action = State.ACTION_BROWSE;
Ben Kwa0574b182015-09-08 07:31:19 -0700141 state.allowMultiple = true;
Steve McKay64ac2512015-05-12 12:49:58 -0700142
Steve McKay459bc2b2015-09-16 15:07:31 -0700143 // Options specific to the DocumentsActivity.
Steve McKay0af8afd2016-02-25 13:34:03 -0800144 assert(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
Steve McKay64ac2512015-05-12 12:49:58 -0700145
Steve McKay4d0255f2015-09-25 16:02:56 -0700146 final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
Steve McKay459bc2b2015-09-16 15:07:31 -0700147 if (stack != null) {
Steve McKayb68dd222015-04-20 17:18:15 -0700148 state.stack = stack;
Steve McKay459bc2b2015-09-16 15:07:31 -0700149 }
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700150 }
151
152 @Override
153 protected void onPostCreate(Bundle savedInstanceState) {
154 super.onPostCreate(savedInstanceState);
Steve McKaya8fa58d2015-12-08 17:02:03 -0800155 // This check avoids a flicker from "Recents" to "Home".
156 // Only update action bar at this point if there is an active
157 // serach. Why? Because this avoid an early (undesired) load of
158 // the recents root...which is the default root in other activities.
159 // In Files app "Home" is the default, but it is loaded async.
Steve McKay18d01e82016-02-03 11:15:57 -0800160 // update will be called once Home root is loaded.
Steve McKaya8fa58d2015-12-08 17:02:03 -0800161 // Except while searching we need this call to ensure the
162 // search bits get layed out correctly.
163 if (mSearchManager.isSearching()) {
Steve McKay18d01e82016-02-03 11:15:57 -0800164 mNavigator.update();
Steve McKaya8fa58d2015-12-08 17:02:03 -0800165 }
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700166 }
167
168 @Override
Steve McKay459bc2b2015-09-16 15:07:31 -0700169 public void onResume() {
170 super.onResume();
171
172 final RootInfo root = getCurrentRoot();
173
174 // If we're browsing a specific root, and that root went away, then we
175 // have no reason to hang around.
176 // TODO: Rather than just disappearing, maybe we should inform
177 // the user what has happened, let them close us. Less surprising.
178 if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
179 finish();
180 }
181 }
182
183 @Override
Steve McKay18d01e82016-02-03 11:15:57 -0800184 public String getDrawerTitle() {
185 return getResources().getString(R.string.files_label);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700186 }
187
188 @Override
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700189 public boolean onPrepareOptionsMenu(Menu menu) {
Steve McKaye9809272015-10-01 11:39:24 -0700190 super.onPrepareOptionsMenu(menu);
Steve McKayd4800812016-02-02 11:41:03 -0800191
Steve McKay36787542016-01-29 18:15:39 -0800192 final RootInfo root = getCurrentRoot();
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700193
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700194 final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700195 final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
Steve McKay36787542016-01-29 18:15:39 -0800196 final MenuItem settings = menu.findItem(R.id.menu_settings);
Steve McKay95c79f52016-02-04 19:40:45 -0800197 final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700198
Steve McKaye9809272015-10-01 11:39:24 -0700199 createDir.setVisible(true);
200 createDir.setEnabled(canCreateDirectory());
Steve McKaye9809272015-10-01 11:39:24 -0700201 pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
Steve McKay36787542016-01-29 18:15:39 -0800202 settings.setVisible(root.hasSettings());
Steve McKay95c79f52016-02-04 19:40:45 -0800203 newWindow.setVisible(true);
Steve McKayceeb3f72015-05-19 16:10:25 -0700204
Steve McKaye9809272015-10-01 11:39:24 -0700205 Menus.disableHiddenItems(menu, pasteFromCb);
206 return true;
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700207 }
208
209 @Override
Steve McKay1f199482015-05-20 15:58:42 -0700210 public boolean onOptionsItemSelected(MenuItem item) {
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700211 switch (item.getItemId()) {
212 case R.id.menu_create_dir:
Steve McKay0af8afd2016-02-25 13:34:03 -0800213 assert(canCreateDirectory());
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700214 showCreateDirectoryDialog();
215 return true;
216 case R.id.menu_new_window:
Steve McKay4d0255f2015-09-25 16:02:56 -0700217 createNewWindow();
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700218 return true;
219 case R.id.menu_paste_from_clipboard:
Steve McKayd4800812016-02-02 11:41:03 -0800220 DirectoryFragment dir = getDirectoryFragment();
221 if (dir != null) {
222 dir.pasteFromClipboard();
223 }
Steve McKayf2c8b0d2015-09-23 15:44:24 -0700224 return true;
Steve McKay1f199482015-05-20 15:58:42 -0700225 }
226
227 return super.onOptionsItemSelected(item);
228 }
229
Steve McKay4d0255f2015-09-25 16:02:56 -0700230 private void createNewWindow() {
Ben Kwa1c0a3892016-01-26 11:50:03 -0800231 Metrics.logMultiWindow(this);
Steve McKay4d0255f2015-09-25 16:02:56 -0700232 Intent intent = LauncherActivity.createLaunchIntent(this);
233 intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
Steve McKaydf5add42016-01-28 12:02:57 -0800234
235 // With new multi-window mode we have to pick how we are launched.
236 // By default we'd be launched in-place above the existing app.
237 // By setting launch-to-side ActivityManager will open us to side.
Wale Ogunwalec2fa6c02016-01-29 17:46:53 -0800238 if (inMultiWindow()) {
Wale Ogunwale7f81a692016-01-30 11:27:21 -0800239 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
Steve McKaydf5add42016-01-28 12:02:57 -0800240 }
241
Steve McKay4d0255f2015-09-25 16:02:56 -0700242 startActivity(intent);
243 }
244
Steve McKay1f199482015-05-20 15:58:42 -0700245 @Override
Aga Wronska8788dad2016-01-15 17:30:15 -0800246 void refreshDirectory(int anim) {
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700247 final FragmentManager fm = getFragmentManager();
248 final RootInfo root = getCurrentRoot();
249 final DocumentInfo cwd = getCurrentDirectory();
250
Steve McKay0af8afd2016-02-25 13:34:03 -0800251 assert(!mSearchManager.isSearching());
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800252
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700253 if (cwd == null) {
254 DirectoryFragment.showRecentsOpen(fm, anim);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700255 } else {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800256 // Normal boring directory
257 DirectoryFragment.showDirectory(fm, root, cwd, anim);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700258 }
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700259 }
260
261 @Override
Steve McKay6bbed4d2015-08-17 13:18:05 -0700262 void onRootPicked(RootInfo root) {
263 super.onRootPicked(root);
264 mDrawer.setOpen(false);
265 }
266
267 @Override
Steve McKay351a7492015-08-04 10:11:01 -0700268 public void onDocumentsPicked(List<DocumentInfo> docs) {
269 throw new UnsupportedOperationException();
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700270 }
271
Steve McKay351a7492015-08-04 10:11:01 -0700272 @Override
Tomasz Mikolajewskid71bd612016-02-16 12:28:43 +0900273 public void onDocumentPicked(DocumentInfo doc, Model model) {
Tomasz Mikolajewski734936a2015-11-25 13:01:18 +0900274 if (doc.isContainer()) {
275 openContainerDocument(doc);
Steve McKay351a7492015-08-04 10:11:01 -0700276 } else {
Tomasz Mikolajewskid71bd612016-02-16 12:28:43 +0900277 openDocument(doc, model);
Steve McKay351a7492015-08-04 10:11:01 -0700278 }
Steve McKayedc65bb2015-07-31 14:35:22 -0700279 }
280
281 /**
282 * Launches an intent to view the specified document.
283 */
Tomasz Mikolajewskid71bd612016-02-16 12:28:43 +0900284 private void openDocument(DocumentInfo doc, Model model) {
285 Intent intent = new QuickViewIntentBuilder(
286 getPackageManager(), getResources(), doc, model).build();
Steve McKay351a7492015-08-04 10:11:01 -0700287
Steve McKay3bd316c2015-10-15 15:27:30 -0700288 if (intent != null) {
289 // TODO: un-work around issue b/24963914. Should be fixed soon.
290 try {
291 startActivity(intent);
292 return;
293 } catch (SecurityException e) {
Steve McKayb4a6b362015-10-23 09:04:09 -0700294 // Carry on to regular view mode.
Steve McKay3bd316c2015-10-15 15:27:30 -0700295 Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
296 }
Steve McKayedc65bb2015-07-31 14:35:22 -0700297 }
298
Steve McKayb4a6b362015-10-23 09:04:09 -0700299 // Fallback to traditional VIEW action...
Steve McKay3bd316c2015-10-15 15:27:30 -0700300 intent = new Intent(Intent.ACTION_VIEW);
301 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
302 intent.setData(doc.derivedUri);
303
Steve McKay351a7492015-08-04 10:11:01 -0700304 if (DEBUG && intent.getClipData() != null) {
305 Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData());
306 }
307
Steve McKayedc65bb2015-07-31 14:35:22 -0700308 try {
309 startActivity(intent);
Steve McKay3bd316c2015-10-15 15:27:30 -0700310 } catch (ActivityNotFoundException e) {
311 Snackbars.makeSnackbar(
312 this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
Steve McKayedc65bb2015-07-31 14:35:22 -0700313 }
314 }
315
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700316 @Override
Steve McKay0599a442015-05-05 14:50:00 -0700317 public boolean onKeyShortcut(int keyCode, KeyEvent event) {
318 DirectoryFragment dir;
Steve McKayd4800812016-02-02 11:41:03 -0800319 // TODO: All key events should be statically bound using alphabeticShortcut.
320 // But not working.
Steve McKay0599a442015-05-05 14:50:00 -0700321 switch (keyCode) {
322 case KeyEvent.KEYCODE_A:
Steve McKayd4800812016-02-02 11:41:03 -0800323 dir = getDirectoryFragment();
324 if (dir != null) {
325 dir.selectAllFiles();
326 }
Steve McKay0599a442015-05-05 14:50:00 -0700327 return true;
Steve McKay1f199482015-05-20 15:58:42 -0700328 case KeyEvent.KEYCODE_C:
Steve McKayd4800812016-02-02 11:41:03 -0800329 dir = getDirectoryFragment();
330 if (dir != null) {
331 dir.copySelectedToClipboard();
332 }
Steve McKay6035b3c2015-12-04 11:19:09 -0800333 return true;
334 case KeyEvent.KEYCODE_V:
Steve McKayd4800812016-02-02 11:41:03 -0800335 dir = getDirectoryFragment();
336 if (dir != null) {
337 dir.pasteFromClipboard();
338 }
Steve McKay6035b3c2015-12-04 11:19:09 -0800339 return true;
340 default:
341 return super.onKeyShortcut(keyCode, event);
Steve McKay0599a442015-05-05 14:50:00 -0700342 }
343 }
344
Steve McKayfd8425a2016-02-23 14:34:50 -0800345 // Do some "do what a I want" drawer fiddling, but don't
346 // do it if user already hit back recently and we recently
347 // did some fiddling.
348 @Override
349 boolean onBeforePopDir() {
350 int size = mState.stack.size();
351
352 if (mDrawer.isPresent()
353 && (System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) {
354 // Close drawer if it is open.
355 if (mDrawer.isOpen()) {
356 mDrawer.setOpen(false);
357 mDrawerLastFiddled = System.currentTimeMillis();
358 return true;
359 }
360
361 // Open the Close drawer if it is closed and we're at the top of a root.
362 if (size == 1) {
363 mDrawer.setOpen(true);
364 // Remember so we don't just close it again if back is pressed again.
365 mDrawerLastFiddled = System.currentTimeMillis();
366 return true;
367 }
368 }
369
370 return false;
371 }
372
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800373 // Turns out only DocumentsActivity was ever calling saveStackBlocking.
374 // There may be a case where we want to contribute entries from
375 // Behavior here in FilesActivity, but it isn't yet obvious.
376 // TODO: Contribute to recents, or remove this.
377 void writeStackToRecentsBlocking() {
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700378 final ContentResolver resolver = getContentResolver();
379 final ContentValues values = new ContentValues();
380
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800381 final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700382
383 // Remember location for next app launch
384 final String packageName = getCallingPackageMaybeExtra();
385 values.clear();
386 values.put(ResumeColumns.STACK, rawStack);
387 values.put(ResumeColumns.EXTERNAL, 0);
388 resolver.insert(RecentsProvider.buildResume(packageName), values);
389 }
390
Steve McKayb68dd222015-04-20 17:18:15 -0700391 @Override
392 void onTaskFinished(Uri... uris) {
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700393 Log.d(TAG, "onFinished() " + Arrays.toString(uris));
394
395 final Intent intent = new Intent();
396 if (uris.length == 1) {
397 intent.setData(uris[0]);
398 } else if (uris.length > 1) {
399 final ClipData clipData = new ClipData(
400 null, mState.acceptMimes, new ClipData.Item(uris[0]));
401 for (int i = 1; i < uris.length; i++) {
402 clipData.addItem(new ClipData.Item(uris[i]));
403 }
404 intent.setClipData(clipData);
405 }
406
407 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
408 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
409 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
410
411 setResult(Activity.RESULT_OK, intent);
412 finish();
413 }
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900414
415 /**
416 * Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
417 * to know which root to select. Also, the stack doesn't contain intermediate directories.
418 * It's primarly used for opening ZIP archives from Downloads app.
419 */
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800420 private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> {
421
422 private final State mState;
423 public OpenUriForViewTask(FilesActivity activity) {
424 super(activity);
425 mState = activity.mState;
426 }
427
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900428 @Override
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800429 protected Void run(Uri... params) {
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900430 final Uri uri = params[0];
431
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800432 final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900433 final String authority = uri.getAuthority();
434
435 final Collection<RootInfo> roots =
436 rootsCache.getRootsForAuthorityBlocking(authority);
437 if (roots.isEmpty()) {
438 Log.e(TAG, "Failed to find root for the requested Uri: " + uri);
439 return null;
440 }
441
442 final RootInfo root = roots.iterator().next();
443 mState.stack.root = root;
444 try {
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800445 mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900446 } catch (FileNotFoundException e) {
447 Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
448 }
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800449 mState.stack.add(mOwner.getRootDocumentBlocking(root));
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900450 return null;
451 }
452
453 @Override
Steve McKayc7dc0cf2016-02-04 12:15:22 -0800454 protected void finish(Void result) {
455 mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
Tomasz Mikolajewski5a1e8792016-01-27 17:36:51 +0900456 }
457 }
Steve McKayd0a2a2c2015-03-25 14:35:33 -0700458}