blob: a3378ad7e3bd3b8e21bbd424a112f9cfe053adc2 [file] [log] [blame]
Steve McKaye934ce62015-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 McKaya7e923c2016-01-28 12:02:57 -080019import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
Steve McKay83df8c02015-09-16 15:07:31 -070020import static com.android.documentsui.Shared.DEBUG;
Steve McKayf8621552015-11-03 15:23:16 -080021import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
Steve McKay83df8c02015-09-16 15:07:31 -070022import static com.android.internal.util.Preconditions.checkArgument;
Steve McKay9f9d5b42015-09-23 15:44:24 -070023import static com.android.internal.util.Preconditions.checkState;
Steve McKayd0d9afc2015-05-06 12:16:40 -070024
Steve McKaye934ce62015-03-25 14:35:33 -070025import android.app.Activity;
Steve McKaye934ce62015-03-25 14:35:33 -070026import android.app.FragmentManager;
27import android.content.ActivityNotFoundException;
28import android.content.ClipData;
Steve McKaye934ce62015-03-25 14:35:33 -070029import android.content.ContentResolver;
30import android.content.ContentValues;
Steve McKaye934ce62015-03-25 14:35:33 -070031import android.content.Intent;
Steve McKaye934ce62015-03-25 14:35:33 -070032import android.net.Uri;
Steve McKaye934ce62015-03-25 14:35:33 -070033import android.os.Bundle;
Steve McKay323ee3e2015-09-25 16:02:56 -070034import android.os.Parcelable;
35import android.provider.DocumentsContract;
Ben Kwa94b486d2015-09-30 10:00:10 -070036import android.support.design.widget.Snackbar;
Steve McKaye934ce62015-03-25 14:35:33 -070037import android.util.Log;
Steve McKay3da8afc2015-05-05 14:50:00 -070038import android.view.KeyEvent;
Steve McKaye934ce62015-03-25 14:35:33 -070039import android.view.Menu;
40import android.view.MenuItem;
Steve McKaye934ce62015-03-25 14:35:33 -070041
Steve McKaya7e923c2016-01-28 12:02:57 -080042import com.android.documentsui.OperationDialogFragment.DialogType;
Steve McKaye934ce62015-03-25 14:35:33 -070043import com.android.documentsui.RecentsProvider.ResumeColumns;
Steve McKayf8621552015-11-03 15:23:16 -080044import com.android.documentsui.dirlist.DirectoryFragment;
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +090045import com.android.documentsui.dirlist.Model;
Steve McKaye934ce62015-03-25 14:35:33 -070046import com.android.documentsui.model.DocumentInfo;
47import com.android.documentsui.model.DocumentStack;
48import com.android.documentsui.model.DurableUtils;
49import com.android.documentsui.model.RootInfo;
Steve McKay14e827a2016-01-06 18:32:13 -080050import com.android.documentsui.services.FileOperationService;
Steve McKaye934ce62015-03-25 14:35:33 -070051
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +090052import java.io.FileNotFoundException;
Tomasz Mikolajewski61686592015-04-13 19:38:43 +090053import java.util.ArrayList;
Steve McKaye934ce62015-03-25 14:35:33 -070054import java.util.Arrays;
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +090055import java.util.Collection;
Steve McKaye934ce62015-03-25 14:35:33 -070056import java.util.List;
Steve McKaye934ce62015-03-25 14:35:33 -070057
Steve McKayef3e2cf2015-04-20 17:18:15 -070058/**
Steve McKay273103b2015-05-12 12:49:58 -070059 * Standalone file management activity.
Steve McKayef3e2cf2015-04-20 17:18:15 -070060 */
Steve McKay12055472015-08-20 16:48:49 -070061public class FilesActivity extends BaseActivity {
Steve McKayc78bcb82015-07-31 14:35:22 -070062
Ben Kwa0f7078f02015-09-08 07:31:19 -070063 public static final String TAG = "FilesActivity";
Steve McKaye934ce62015-03-25 14:35:33 -070064
Steve McKayc95d87c2016-02-23 14:34:50 -080065 // See comments where this const is referenced for details.
66 private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
67
68 // Track the time we opened the drawer in response to back being pressed.
69 // We use the time gap to figure out whether to close app or reopen the drawer.
70 private long mDrawerLastFiddled;
Steve McKaybdbd0ff2015-05-20 15:58:42 -070071 private DocumentClipper mClipper;
Steve McKayef3e2cf2015-04-20 17:18:15 -070072
Steve McKay12055472015-08-20 16:48:49 -070073 public FilesActivity() {
74 super(R.layout.files_activity, TAG);
Steve McKayef3e2cf2015-04-20 17:18:15 -070075 }
Steve McKaye934ce62015-03-25 14:35:33 -070076
77 @Override
78 public void onCreate(Bundle icicle) {
Steve McKaye934ce62015-03-25 14:35:33 -070079 super.onCreate(icicle);
80
Steve McKay12055472015-08-20 16:48:49 -070081 mClipper = new DocumentClipper(this);
Steve McKaybdbd0ff2015-05-20 15:58:42 -070082
Steve McKaye934ce62015-03-25 14:35:33 -070083 RootsFragment.show(getFragmentManager(), null);
Steve McKay83df8c02015-09-16 15:07:31 -070084
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +090085 final Intent intent = getIntent();
86 final Uri uri = intent.getData();
Steve McKay323ee3e2015-09-25 16:02:56 -070087
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +090088 if (mState.restored) {
89 if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +090090 } else if (!mState.stack.isEmpty()) {
Steve McKay323ee3e2015-09-25 16:02:56 -070091 // If a non-empty stack is present in our state it was read (presumably)
92 // from EXTRA_STACK intent extra. In this case, we'll skip other means of
93 // loading or restoring the stack.
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +090094 //
95 // When restoring from a stack, if a URI is present, it should only ever
Tomasz Mikolajewskicd270152016-02-01 12:01:14 +090096 // be a launch URI, or a fake Uri from notifications.
97 // Launch URIs support sensible activity management, but don't specify a real
98 // content target.
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +090099 if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
Tomasz Mikolajewskicd270152016-02-01 12:01:14 +0900100 checkState(uri == null || uri.getAuthority() == null ||
101 LauncherActivity.isLaunchUri(uri));
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800102 refreshCurrentRootAndDirectory(ANIM_NONE);
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900103 } else if (intent.getAction() == Intent.ACTION_VIEW) {
104 checkArgument(uri != null);
Steve McKay95cd85a2016-02-04 12:15:22 -0800105 new OpenUriForViewTask(this).executeOnExecutor(
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900106 ProviderExecutor.forAuthority(uri.getAuthority()), uri);
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900107 } else if (DocumentsContract.isRootUri(this, uri)) {
108 if (DEBUG) Log.d(TAG, "Launching with root URI.");
109 // If we've got a specific root to display, restore that root using a dedicated
110 // authority. That way a misbehaving provider won't result in an ANR.
Steve McKayc95d87c2016-02-23 14:34:50 -0800111 loadRoot(uri);
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900112 } else {
113 if (DEBUG) Log.d(TAG, "Launching into Home directory.");
114 // If all else fails, try to load "Home" directory.
115 final Uri homeUri = DocumentsContract.buildHomeUri();
Steve McKayc95d87c2016-02-23 14:34:50 -0800116 loadRoot(homeUri);
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900117 }
Tomasz Mikolajewskif8c3f322015-04-14 16:32:41 +0900118
Tomasz Mikolajewski748ea8c2016-01-22 16:22:51 +0900119 final @DialogType int dialogType = intent.getIntExtra(
120 FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900121 // DialogFragment takes care of restoring the dialog on configuration change.
122 // Only show it manually for the first time (icicle is null).
Tomasz Mikolajewski748ea8c2016-01-22 16:22:51 +0900123 if (icicle == null && dialogType != DIALOG_TYPE_UNKNOWN) {
124 final int opType = intent.getIntExtra(
125 FileOperationService.EXTRA_OPERATION,
126 FileOperationService.OPERATION_COPY);
127 final ArrayList<DocumentInfo> srcList =
Steve McKay14e827a2016-01-06 18:32:13 -0800128 intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
Tomasz Mikolajewski748ea8c2016-01-22 16:22:51 +0900129 OperationDialogFragment.show(
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900130 getFragmentManager(),
Tomasz Mikolajewski748ea8c2016-01-22 16:22:51 +0900131 dialogType,
132 srcList,
Tomasz Mikolajewskiaa684452015-12-25 17:06:52 +0900133 mState.stack,
Steve McKay14e827a2016-01-06 18:32:13 -0800134 opType);
Steve McKaye934ce62015-03-25 14:35:33 -0700135 }
136 }
137
Steve McKay12055472015-08-20 16:48:49 -0700138 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800139 void includeState(State state) {
Steve McKaye934ce62015-03-25 14:35:33 -0700140 final Intent intent = getIntent();
Ben Kwa0f7078f02015-09-08 07:31:19 -0700141
Steve McKay83df8c02015-09-16 15:07:31 -0700142 state.action = State.ACTION_BROWSE;
Ben Kwa0f7078f02015-09-08 07:31:19 -0700143 state.allowMultiple = true;
Steve McKay273103b2015-05-12 12:49:58 -0700144
Steve McKay83df8c02015-09-16 15:07:31 -0700145 // Options specific to the DocumentsActivity.
146 checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
Steve McKay273103b2015-05-12 12:49:58 -0700147
Steve McKay323ee3e2015-09-25 16:02:56 -0700148 final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
Steve McKay83df8c02015-09-16 15:07:31 -0700149 if (stack != null) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700150 state.stack = stack;
Steve McKay83df8c02015-09-16 15:07:31 -0700151 }
Steve McKaye934ce62015-03-25 14:35:33 -0700152 }
153
154 @Override
155 protected void onPostCreate(Bundle savedInstanceState) {
156 super.onPostCreate(savedInstanceState);
Steve McKayb67bfbf2015-12-08 17:02:03 -0800157 // This check avoids a flicker from "Recents" to "Home".
158 // Only update action bar at this point if there is an active
159 // serach. Why? Because this avoid an early (undesired) load of
160 // the recents root...which is the default root in other activities.
161 // In Files app "Home" is the default, but it is loaded async.
Steve McKay1f264a82016-02-03 11:15:57 -0800162 // update will be called once Home root is loaded.
Steve McKayb67bfbf2015-12-08 17:02:03 -0800163 // Except while searching we need this call to ensure the
164 // search bits get layed out correctly.
165 if (mSearchManager.isSearching()) {
Steve McKay1f264a82016-02-03 11:15:57 -0800166 mNavigator.update();
Steve McKayb67bfbf2015-12-08 17:02:03 -0800167 }
Steve McKaye934ce62015-03-25 14:35:33 -0700168 }
169
170 @Override
Steve McKay83df8c02015-09-16 15:07:31 -0700171 public void onResume() {
172 super.onResume();
173
174 final RootInfo root = getCurrentRoot();
175
176 // If we're browsing a specific root, and that root went away, then we
177 // have no reason to hang around.
178 // TODO: Rather than just disappearing, maybe we should inform
179 // the user what has happened, let them close us. Less surprising.
180 if (mRoots.getRootBlocking(root.authority, root.rootId) == null) {
181 finish();
182 }
183 }
184
185 @Override
Steve McKay1f264a82016-02-03 11:15:57 -0800186 public String getDrawerTitle() {
187 return getResources().getString(R.string.files_label);
Steve McKaye934ce62015-03-25 14:35:33 -0700188 }
189
190 @Override
Steve McKaye934ce62015-03-25 14:35:33 -0700191 public boolean onPrepareOptionsMenu(Menu menu) {
Steve McKay5bbae102015-10-01 11:39:24 -0700192 super.onPrepareOptionsMenu(menu);
Steve McKay3ce95952016-02-02 11:41:03 -0800193
Steve McKayefa17612016-01-29 18:15:39 -0800194 final RootInfo root = getCurrentRoot();
Steve McKay9f9d5b42015-09-23 15:44:24 -0700195
Steve McKaye934ce62015-03-25 14:35:33 -0700196 final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
Steve McKay9f9d5b42015-09-23 15:44:24 -0700197 final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
Steve McKayefa17612016-01-29 18:15:39 -0800198 final MenuItem settings = menu.findItem(R.id.menu_settings);
Steve McKayf8737692016-02-04 19:40:45 -0800199 final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
Steve McKaye934ce62015-03-25 14:35:33 -0700200
Steve McKay5bbae102015-10-01 11:39:24 -0700201 createDir.setVisible(true);
202 createDir.setEnabled(canCreateDirectory());
Steve McKay5bbae102015-10-01 11:39:24 -0700203 pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
Steve McKayefa17612016-01-29 18:15:39 -0800204 settings.setVisible(root.hasSettings());
Steve McKayf8737692016-02-04 19:40:45 -0800205 newWindow.setVisible(true);
Steve McKaya521d0d2015-05-19 16:10:25 -0700206
Steve McKay5bbae102015-10-01 11:39:24 -0700207 Menus.disableHiddenItems(menu, pasteFromCb);
208 return true;
Steve McKaye934ce62015-03-25 14:35:33 -0700209 }
210
211 @Override
Steve McKaybdbd0ff2015-05-20 15:58:42 -0700212 public boolean onOptionsItemSelected(MenuItem item) {
Steve McKay9f9d5b42015-09-23 15:44:24 -0700213 switch (item.getItemId()) {
214 case R.id.menu_create_dir:
215 checkState(canCreateDirectory());
216 showCreateDirectoryDialog();
217 return true;
218 case R.id.menu_new_window:
Steve McKay323ee3e2015-09-25 16:02:56 -0700219 createNewWindow();
Steve McKay9f9d5b42015-09-23 15:44:24 -0700220 return true;
221 case R.id.menu_paste_from_clipboard:
Steve McKay3ce95952016-02-02 11:41:03 -0800222 DirectoryFragment dir = getDirectoryFragment();
223 if (dir != null) {
224 dir.pasteFromClipboard();
225 }
Steve McKay9f9d5b42015-09-23 15:44:24 -0700226 return true;
Steve McKaybdbd0ff2015-05-20 15:58:42 -0700227 }
228
229 return super.onOptionsItemSelected(item);
230 }
231
Steve McKay323ee3e2015-09-25 16:02:56 -0700232 private void createNewWindow() {
Ben Kwa72379982016-01-26 11:50:03 -0800233 Metrics.logMultiWindow(this);
Steve McKay323ee3e2015-09-25 16:02:56 -0700234 Intent intent = LauncherActivity.createLaunchIntent(this);
235 intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
Steve McKaya7e923c2016-01-28 12:02:57 -0800236
237 // With new multi-window mode we have to pick how we are launched.
238 // By default we'd be launched in-place above the existing app.
239 // By setting launch-to-side ActivityManager will open us to side.
Wale Ogunwale3b93a4d2016-01-29 17:46:53 -0800240 if (inMultiWindow()) {
Wale Ogunwale2a25a622016-01-30 11:27:21 -0800241 intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
Steve McKaya7e923c2016-01-28 12:02:57 -0800242 }
243
Steve McKay323ee3e2015-09-25 16:02:56 -0700244 startActivity(intent);
245 }
246
Steve McKaybdbd0ff2015-05-20 15:58:42 -0700247 @Override
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800248 void refreshDirectory(int anim) {
Steve McKaye934ce62015-03-25 14:35:33 -0700249 final FragmentManager fm = getFragmentManager();
250 final RootInfo root = getCurrentRoot();
251 final DocumentInfo cwd = getCurrentDirectory();
252
Aga Wronska893390b2016-02-17 13:50:42 -0800253 if (DEBUG) checkState(!mSearchManager.isSearching());
254
Steve McKaye934ce62015-03-25 14:35:33 -0700255 if (cwd == null) {
256 DirectoryFragment.showRecentsOpen(fm, anim);
Steve McKaye934ce62015-03-25 14:35:33 -0700257 } else {
Aga Wronska893390b2016-02-17 13:50:42 -0800258 // Normal boring directory
259 DirectoryFragment.showDirectory(fm, root, cwd, anim);
Steve McKaye934ce62015-03-25 14:35:33 -0700260 }
Steve McKaye934ce62015-03-25 14:35:33 -0700261 }
262
263 @Override
Steve McKay4f160402015-08-17 13:18:05 -0700264 void onRootPicked(RootInfo root) {
265 super.onRootPicked(root);
266 mDrawer.setOpen(false);
267 }
268
269 @Override
Steve McKay6eaf38632015-08-04 10:11:01 -0700270 public void onDocumentsPicked(List<DocumentInfo> docs) {
271 throw new UnsupportedOperationException();
Steve McKaye934ce62015-03-25 14:35:33 -0700272 }
273
Steve McKay6eaf38632015-08-04 10:11:01 -0700274 @Override
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +0900275 public void onDocumentPicked(DocumentInfo doc, Model model) {
Tomasz Mikolajewski39acff52015-11-25 13:01:18 +0900276 if (doc.isContainer()) {
277 openContainerDocument(doc);
Steve McKay6eaf38632015-08-04 10:11:01 -0700278 } else {
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +0900279 openDocument(doc, model);
Steve McKay6eaf38632015-08-04 10:11:01 -0700280 }
Steve McKayc78bcb82015-07-31 14:35:22 -0700281 }
282
283 /**
284 * Launches an intent to view the specified document.
285 */
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +0900286 private void openDocument(DocumentInfo doc, Model model) {
287 Intent intent = new QuickViewIntentBuilder(
288 getPackageManager(), getResources(), doc, model).build();
Steve McKay6eaf38632015-08-04 10:11:01 -0700289
Steve McKay3b2ad112015-10-15 15:27:30 -0700290 if (intent != null) {
291 // TODO: un-work around issue b/24963914. Should be fixed soon.
292 try {
293 startActivity(intent);
294 return;
295 } catch (SecurityException e) {
Steve McKay1eafb662015-10-23 09:04:09 -0700296 // Carry on to regular view mode.
Steve McKay3b2ad112015-10-15 15:27:30 -0700297 Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
298 }
Steve McKayc78bcb82015-07-31 14:35:22 -0700299 }
300
Steve McKay1eafb662015-10-23 09:04:09 -0700301 // Fallback to traditional VIEW action...
Steve McKay3b2ad112015-10-15 15:27:30 -0700302 intent = new Intent(Intent.ACTION_VIEW);
303 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
304 intent.setData(doc.derivedUri);
305
Steve McKay6eaf38632015-08-04 10:11:01 -0700306 if (DEBUG && intent.getClipData() != null) {
307 Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData());
308 }
309
Steve McKayc78bcb82015-07-31 14:35:22 -0700310 try {
311 startActivity(intent);
Steve McKay3b2ad112015-10-15 15:27:30 -0700312 } catch (ActivityNotFoundException e) {
313 Snackbars.makeSnackbar(
314 this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
Steve McKayc78bcb82015-07-31 14:35:22 -0700315 }
316 }
317
Steve McKaye934ce62015-03-25 14:35:33 -0700318 @Override
Steve McKay3da8afc2015-05-05 14:50:00 -0700319 public boolean onKeyShortcut(int keyCode, KeyEvent event) {
320 DirectoryFragment dir;
Steve McKay3ce95952016-02-02 11:41:03 -0800321 // TODO: All key events should be statically bound using alphabeticShortcut.
322 // But not working.
Steve McKay3da8afc2015-05-05 14:50:00 -0700323 switch (keyCode) {
324 case KeyEvent.KEYCODE_A:
Steve McKay3ce95952016-02-02 11:41:03 -0800325 dir = getDirectoryFragment();
326 if (dir != null) {
327 dir.selectAllFiles();
328 }
Steve McKay3da8afc2015-05-05 14:50:00 -0700329 return true;
Steve McKaybdbd0ff2015-05-20 15:58:42 -0700330 case KeyEvent.KEYCODE_C:
Steve McKay3ce95952016-02-02 11:41:03 -0800331 dir = getDirectoryFragment();
332 if (dir != null) {
333 dir.copySelectedToClipboard();
334 }
Steve McKay8fd086a2015-12-04 11:19:09 -0800335 return true;
336 case KeyEvent.KEYCODE_V:
Steve McKay3ce95952016-02-02 11:41:03 -0800337 dir = getDirectoryFragment();
338 if (dir != null) {
339 dir.pasteFromClipboard();
340 }
Steve McKay8fd086a2015-12-04 11:19:09 -0800341 return true;
342 default:
343 return super.onKeyShortcut(keyCode, event);
Steve McKay3da8afc2015-05-05 14:50:00 -0700344 }
345 }
346
Steve McKayc95d87c2016-02-23 14:34:50 -0800347 // Do some "do what a I want" drawer fiddling, but don't
348 // do it if user already hit back recently and we recently
349 // did some fiddling.
350 @Override
351 boolean onBeforePopDir() {
352 int size = mState.stack.size();
353
354 if (mDrawer.isPresent()
355 && (System.currentTimeMillis() - mDrawerLastFiddled) > DRAWER_NO_FIDDLE_DELAY) {
356 // Close drawer if it is open.
357 if (mDrawer.isOpen()) {
358 mDrawer.setOpen(false);
359 mDrawerLastFiddled = System.currentTimeMillis();
360 return true;
361 }
362
363 // Open the Close drawer if it is closed and we're at the top of a root.
364 if (size == 1) {
365 mDrawer.setOpen(true);
366 // Remember so we don't just close it again if back is pressed again.
367 mDrawerLastFiddled = System.currentTimeMillis();
368 return true;
369 }
370 }
371
372 return false;
373 }
374
Steve McKay95cd85a2016-02-04 12:15:22 -0800375 // Turns out only DocumentsActivity was ever calling saveStackBlocking.
376 // There may be a case where we want to contribute entries from
377 // Behavior here in FilesActivity, but it isn't yet obvious.
378 // TODO: Contribute to recents, or remove this.
379 void writeStackToRecentsBlocking() {
Steve McKaye934ce62015-03-25 14:35:33 -0700380 final ContentResolver resolver = getContentResolver();
381 final ContentValues values = new ContentValues();
382
Steve McKay95cd85a2016-02-04 12:15:22 -0800383 final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
Steve McKaye934ce62015-03-25 14:35:33 -0700384
385 // Remember location for next app launch
386 final String packageName = getCallingPackageMaybeExtra();
387 values.clear();
388 values.put(ResumeColumns.STACK, rawStack);
389 values.put(ResumeColumns.EXTERNAL, 0);
390 resolver.insert(RecentsProvider.buildResume(packageName), values);
391 }
392
Steve McKayef3e2cf2015-04-20 17:18:15 -0700393 @Override
394 void onTaskFinished(Uri... uris) {
Steve McKaye934ce62015-03-25 14:35:33 -0700395 Log.d(TAG, "onFinished() " + Arrays.toString(uris));
396
397 final Intent intent = new Intent();
398 if (uris.length == 1) {
399 intent.setData(uris[0]);
400 } else if (uris.length > 1) {
401 final ClipData clipData = new ClipData(
402 null, mState.acceptMimes, new ClipData.Item(uris[0]));
403 for (int i = 1; i < uris.length; i++) {
404 clipData.addItem(new ClipData.Item(uris[i]));
405 }
406 intent.setClipData(clipData);
407 }
408
409 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
410 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
411 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
412
413 setResult(Activity.RESULT_OK, intent);
414 finish();
415 }
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900416
417 /**
418 * Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
419 * to know which root to select. Also, the stack doesn't contain intermediate directories.
420 * It's primarly used for opening ZIP archives from Downloads app.
421 */
Steve McKay95cd85a2016-02-04 12:15:22 -0800422 private static final class OpenUriForViewTask extends PairedTask<FilesActivity, Uri, Void> {
423
424 private final State mState;
425 public OpenUriForViewTask(FilesActivity activity) {
426 super(activity);
427 mState = activity.mState;
428 }
429
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900430 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800431 protected Void run(Uri... params) {
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900432 final Uri uri = params[0];
433
Steve McKay95cd85a2016-02-04 12:15:22 -0800434 final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900435 final String authority = uri.getAuthority();
436
437 final Collection<RootInfo> roots =
438 rootsCache.getRootsForAuthorityBlocking(authority);
439 if (roots.isEmpty()) {
440 Log.e(TAG, "Failed to find root for the requested Uri: " + uri);
441 return null;
442 }
443
444 final RootInfo root = roots.iterator().next();
445 mState.stack.root = root;
446 try {
Steve McKay95cd85a2016-02-04 12:15:22 -0800447 mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900448 } catch (FileNotFoundException e) {
449 Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
450 }
Steve McKay95cd85a2016-02-04 12:15:22 -0800451 mState.stack.add(mOwner.getRootDocumentBlocking(root));
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900452 return null;
453 }
454
455 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800456 protected void finish(Void result) {
457 mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
Tomasz Mikolajewskia6120da2016-01-27 17:36:51 +0900458 }
459 }
Steve McKaye934ce62015-03-25 14:35:33 -0700460}