blob: ec7dde9230c450ebc4d4ed73dd3efcdaca353dee [file] [log] [blame]
Jeff Sharkey9e0036e2013-04-26 16:54:55 -07001/*
2 * Copyright (C) 2013 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 McKay95cd85a2016-02-04 12:15:22 -080019import static com.android.documentsui.Shared.DEBUG;
Steve McKay7a3b88c2015-09-23 17:21:40 -070020import static com.android.documentsui.State.ACTION_CREATE;
21import static com.android.documentsui.State.ACTION_GET_CONTENT;
22import static com.android.documentsui.State.ACTION_OPEN;
Steve McKay7a3b88c2015-09-23 17:21:40 -070023import static com.android.documentsui.State.ACTION_OPEN_TREE;
Ben Kwa862b96412015-12-07 13:25:27 -080024import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
Steve McKayf8621552015-11-03 15:23:16 -080025import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
Jeff Sharkey59d577a2015-04-11 21:27:21 -070026
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070027import android.app.Activity;
Steve McKayef3e2cf2015-04-20 17:18:15 -070028import android.app.Fragment;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070029import android.app.FragmentManager;
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -070030import android.content.ClipData;
Jeff Sharkey54ca29a2013-08-15 11:24:03 -070031import android.content.ComponentName;
Jeff Sharkey7aa76012013-09-30 14:26:27 -070032import android.content.ContentProviderClient;
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -070033import android.content.ContentResolver;
Jeff Sharkey20d96d82013-07-30 17:08:39 -070034import android.content.ContentValues;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070035import android.content.Intent;
Jeff Sharkey54ca29a2013-08-15 11:24:03 -070036import android.content.pm.ResolveInfo;
Steve McKay95cd85a2016-02-04 12:15:22 -080037import android.database.Cursor;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070038import android.net.Uri;
39import android.os.Bundle;
Tomasz Mikolajewskie1a03f82015-04-10 10:30:33 +090040import android.os.Parcelable;
Jeff Sharkeya5599ef2013-08-15 16:17:41 -070041import android.provider.DocumentsContract;
Ben Kwa94b486d2015-09-30 10:00:10 -070042import android.support.design.widget.Snackbar;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070043import android.util.Log;
Jeff Sharkey54e55b72013-06-30 20:02:59 -070044import android.view.Menu;
45import android.view.MenuItem;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070046
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070047import com.android.documentsui.RecentsProvider.RecentColumns;
48import com.android.documentsui.RecentsProvider.ResumeColumns;
Steve McKayf8621552015-11-03 15:23:16 -080049import com.android.documentsui.dirlist.DirectoryFragment;
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +090050import com.android.documentsui.dirlist.Model;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070051import com.android.documentsui.model.DocumentInfo;
Jeff Sharkeyb5133112013-09-01 18:41:04 -070052import com.android.documentsui.model.DurableUtils;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070053import com.android.documentsui.model.RootInfo;
Steve McKay14e827a2016-01-06 18:32:13 -080054import com.android.documentsui.services.FileOperationService;
Jeff Sharkey20d96d82013-07-30 17:08:39 -070055
Steve McKay95cd85a2016-02-04 12:15:22 -080056import libcore.io.IoUtils;
57
58import java.io.FileNotFoundException;
59import java.io.IOException;
Ben Kwacc345212015-05-18 13:18:05 -070060import java.util.Arrays;
Steve McKay95cd85a2016-02-04 12:15:22 -080061import java.util.Collection;
Ben Kwacc345212015-05-18 13:18:05 -070062import java.util.List;
63
Steve McKaye934ce62015-03-25 14:35:33 -070064public class DocumentsActivity extends BaseActivity {
Steve McKayef3e2cf2015-04-20 17:18:15 -070065 private static final int CODE_FORWARD = 42;
Ben Kwa0f7078f02015-09-08 07:31:19 -070066 private static final String TAG = "DocumentsActivity";
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070067
Steve McKayef3e2cf2015-04-20 17:18:15 -070068 public DocumentsActivity() {
Steve McKay1f264a82016-02-03 11:15:57 -080069 super(R.layout.documents_activity, TAG);
Steve McKayef3e2cf2015-04-20 17:18:15 -070070 }
71
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070072 @Override
73 public void onCreate(Bundle icicle) {
Steve McKay12055472015-08-20 16:48:49 -070074 super.onCreate(icicle);
Oren Blasberg9e9c2462015-06-25 19:00:43 -070075
Jeff Sharkeyb5133112013-09-01 18:41:04 -070076 if (mState.action == ACTION_CREATE) {
77 final String mimeType = getIntent().getType();
78 final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
79 SaveFragment.show(getFragmentManager(), mimeType, title);
Daichi Hironobbe22922015-04-10 15:50:38 +090080 } else if (mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -070081 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -070082 PickFragment.show(getFragmentManager());
Jeff Sharkeyb5133112013-09-01 18:41:04 -070083 }
84
85 if (mState.action == ACTION_GET_CONTENT) {
86 final Intent moreApps = new Intent(getIntent());
87 moreApps.setComponent(null);
88 moreApps.setPackage(null);
89 RootsFragment.show(getFragmentManager(), moreApps);
Daichi Hironobbe22922015-04-10 15:50:38 +090090 } else if (mState.action == ACTION_OPEN ||
91 mState.action == ACTION_CREATE ||
92 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -070093 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -070094 RootsFragment.show(getFragmentManager(), null);
95 }
96
Jeff Sharkeya9ce0492013-09-19 15:25:56 -070097 if (!mState.restored) {
Makoto Onuki9feac142015-06-23 14:29:25 -070098 // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
99 // talkback from reading aloud the default title, we clear it here.
100 setTitle("");
Steve McKay95cd85a2016-02-04 12:15:22 -0800101 new RestoreStackTask(this).execute();
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700102 } else {
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800103 refreshCurrentRootAndDirectory(ANIM_NONE);
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700104 }
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700105 }
106
Steve McKay12055472015-08-20 16:48:49 -0700107 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800108 void includeState(State state) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700109 final Intent intent = getIntent();
110 final String action = intent.getAction();
111 if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700112 state.action = ACTION_OPEN;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700113 } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700114 state.action = ACTION_CREATE;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700115 } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700116 state.action = ACTION_GET_CONTENT;
Jeff Sharkeyb9fbb722014-06-04 16:42:47 -0700117 } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700118 state.action = ACTION_OPEN_TREE;
Ben Kwa84cebbe2015-09-25 14:48:29 -0700119 } else if (Shared.ACTION_PICK_COPY_DESTINATION.equals(action)) {
120 state.action = ACTION_PICK_COPY_DESTINATION;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700121 }
122
Steve McKayef3e2cf2015-04-20 17:18:15 -0700123 if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
124 state.allowMultiple = intent.getBooleanExtra(
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700125 Intent.EXTRA_ALLOW_MULTIPLE, false);
126 }
127
Tomasz Mikolajewskia8057a92015-11-16 11:41:28 +0900128 if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT
129 || state.action == ACTION_CREATE) {
130 state.openableOnly = intent.hasCategory(Intent.CATEGORY_OPENABLE);
131 }
132
Ben Kwa84cebbe2015-09-25 14:48:29 -0700133 if (state.action == ACTION_PICK_COPY_DESTINATION) {
Steve McKaya6bbeab2016-02-17 15:02:01 -0800134 // Indicates that a copy operation (or move) includes a directory.
135 // Why? Directory creation isn't supported by some roots (like Downloads).
136 // This allows us to restrict available roots to just those with support.
Steve McKayef3e2cf2015-04-20 17:18:15 -0700137 state.directoryCopy = intent.getBooleanExtra(
Ben Kwa84cebbe2015-09-25 14:48:29 -0700138 Shared.EXTRA_DIRECTORY_COPY, false);
Steve McKaya6bbeab2016-02-17 15:02:01 -0800139 state.copyOperationSubType = intent.getIntExtra(
140 FileOperationService.EXTRA_OPERATION,
Steve McKay14e827a2016-01-06 18:32:13 -0800141 FileOperationService.OPERATION_COPY);
Daichi Hirono9be34292015-04-14 17:12:54 +0900142 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700143 }
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700144
Steve McKayaa15dae2016-02-09 16:17:24 -0800145 private void onStackRestored(boolean restored, boolean external) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700146 // Show drawer when no stack restored, but only when requesting
147 // non-visual content. However, if we last used an external app,
148 // drawer is always shown.
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700149
Steve McKayef3e2cf2015-04-20 17:18:15 -0700150 boolean showDrawer = false;
151 if (!restored) {
152 showDrawer = true;
153 }
154 if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
155 showDrawer = false;
156 }
157 if (external && mState.action == ACTION_GET_CONTENT) {
158 showDrawer = true;
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700159 }
Steve McKaya6bbeab2016-02-17 15:02:01 -0800160 if (mState.action == ACTION_PICK_COPY_DESTINATION) {
161 showDrawer = true;
162 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700163
Steve McKayef3e2cf2015-04-20 17:18:15 -0700164 if (showDrawer) {
Steve McKay1f264a82016-02-03 11:15:57 -0800165 mNavigator.revealRootsDrawer(true);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700166 }
167 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700168
Steve McKayef3e2cf2015-04-20 17:18:15 -0700169 public void onAppPicked(ResolveInfo info) {
170 final Intent intent = new Intent(getIntent());
171 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
172 intent.setComponent(new ComponentName(
173 info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
174 startActivityForResult(intent, CODE_FORWARD);
175 }
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700176
Steve McKayef3e2cf2015-04-20 17:18:15 -0700177 @Override
178 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
179 Log.d(TAG, "onActivityResult() code=" + resultCode);
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700180
Steve McKayef3e2cf2015-04-20 17:18:15 -0700181 // Only relay back results when not canceled; otherwise stick around to
182 // let the user pick another app/backend.
183 if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700184
Steve McKayef3e2cf2015-04-20 17:18:15 -0700185 // Remember that we last picked via external app
186 final String packageName = getCallingPackageMaybeExtra();
187 final ContentValues values = new ContentValues();
188 values.put(ResumeColumns.EXTERNAL, 1);
189 getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
190
191 // Pass back result to original caller
192 setResult(resultCode, data);
193 finish();
194 } else {
195 super.onActivityResult(requestCode, resultCode, data);
Jeff Sharkeya5599ef2013-08-15 16:17:41 -0700196 }
197 }
198
Jeff Sharkeybc2971d2013-07-31 20:53:22 -0700199 @Override
200 protected void onPostCreate(Bundle savedInstanceState) {
201 super.onPostCreate(savedInstanceState);
Steve McKay1f264a82016-02-03 11:15:57 -0800202 mDrawer.update();
203 mNavigator.update();
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700204 }
205
Steve McKayef3e2cf2015-04-20 17:18:15 -0700206 @Override
Steve McKay1f264a82016-02-03 11:15:57 -0800207 public String getDrawerTitle() {
208 String title = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
209 if (title == null) {
210 if (mState.action == ACTION_OPEN ||
211 mState.action == ACTION_GET_CONTENT ||
212 mState.action == ACTION_OPEN_TREE) {
213 title = getResources().getString(R.string.title_open);
214 } else if (mState.action == ACTION_CREATE ||
215 mState.action == ACTION_PICK_COPY_DESTINATION) {
216 title = getResources().getString(R.string.title_save);
Tomasz Mikolajewski0e591f92015-06-12 16:22:17 -0700217 } else {
Steve McKay1f264a82016-02-03 11:15:57 -0800218 // If all else fails, just call it "Files".
219 title = getResources().getString(R.string.files_label);
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700220 }
Jeff Sharkeyf52773f2014-07-28 16:38:52 -0700221 }
Jeff Sharkeyef7184a2013-08-05 17:56:48 -0700222
Steve McKay1f264a82016-02-03 11:15:57 -0800223 return title;
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700224 }
225
226 @Override
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700227 public boolean onPrepareOptionsMenu(Menu menu) {
228 super.onPrepareOptionsMenu(menu);
229
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700230 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700231
Steve McKay3ce95952016-02-02 11:41:03 -0800232 boolean picking = mState.action == ACTION_CREATE
233 || mState.action == ACTION_OPEN_TREE
234 || mState.action == ACTION_PICK_COPY_DESTINATION;
235
236 if (picking) {
237 // May already be hidden because the root
238 // doesn't support search.
239 mSearchManager.showMenu(false);
240 }
241
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700242 final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
Jeff Sharkeyded77182013-09-03 14:17:06 -0700243 final MenuItem grid = menu.findItem(R.id.menu_grid);
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700244 final MenuItem list = menu.findItem(R.id.menu_list);
Jeff Sharkey669f8e72014-08-08 15:10:03 -0700245 final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700246
Steve McKay5bbae102015-10-01 11:39:24 -0700247 boolean recents = cwd == null;
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700248
Steve McKay5bbae102015-10-01 11:39:24 -0700249 createDir.setVisible(picking && !recents && cwd.isCreateSupported());
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700250
Steve McKay5bbae102015-10-01 11:39:24 -0700251 // No display options in recent directories
Steve McKay7c7e6842016-01-08 14:58:33 -0800252 if (picking && recents) {
253 grid.setVisible(false);
254 list.setVisible(false);
255 }
Steve McKay5bbae102015-10-01 11:39:24 -0700256
257 fileSize.setVisible(fileSize.isVisible() && !picking);
Steve McKay5bbae102015-10-01 11:39:24 -0700258
259 if (mState.action == ACTION_CREATE) {
260 final FragmentManager fm = getFragmentManager();
Steve McKay0c2a0a82015-10-14 17:41:13 -0700261 SaveFragment.get(fm).prepareForDirectory(cwd);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700262 }
Jeff Sharkeybc2971d2013-07-31 20:53:22 -0700263
Steve McKay5bbae102015-10-01 11:39:24 -0700264 Menus.disableHiddenItems(menu);
Jeff Sharkey1407d4c2015-04-12 21:52:24 -0700265
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700266 return true;
267 }
268
269 @Override
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800270 void refreshDirectory(int anim) {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700271 final FragmentManager fm = getFragmentManager();
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700272 final RootInfo root = getCurrentRoot();
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700273 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700274
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700275 if (cwd == null) {
276 // No directory means recents
Daichi Hironobbe22922015-04-10 15:50:38 +0900277 if (mState.action == ACTION_CREATE ||
278 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700279 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700280 RecentsCreateFragment.show(fm);
281 } else {
Jeff Sharkeye20a3ac2013-09-18 16:26:49 -0700282 DirectoryFragment.showRecentsOpen(fm, anim);
Jeff Sharkey88f322c2013-09-12 16:25:02 -0700283
Steve McKay3eb2d072016-01-25 19:00:22 -0800284 // In recents we pick layout mode based on the mimetype,
285 // picking GRID for visual types. We intentionally don't
286 // consult a user's saved preferences here since they are
287 // set per root (not per root and per mimetype).
288 boolean visualMimes = MimePredicate.mimeMatches(
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700289 MimePredicate.VISUAL_MIMES, mState.acceptMimes);
Steve McKay3eb2d072016-01-25 19:00:22 -0800290 mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700291 }
292 } else {
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800293 if (mSearchManager.isSearching()) {
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700294 // Ongoing search
Jeff Sharkeye20a3ac2013-09-18 16:26:49 -0700295 DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700296 } else {
297 // Normal boring directory
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800298 DirectoryFragment.showDirectory(fm, root, cwd, anim);
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700299 }
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700300 }
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700301
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700302 // Forget any replacement target
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700303 if (mState.action == ACTION_CREATE) {
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700304 final SaveFragment save = SaveFragment.get(fm);
305 if (save != null) {
306 save.setReplaceTarget(null);
307 }
308 }
309
Daichi Hironobbe22922015-04-10 15:50:38 +0900310 if (mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700311 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700312 final PickFragment pick = PickFragment.get(fm);
313 if (pick != null) {
Steve McKaya6bbeab2016-02-17 15:02:01 -0800314 pick.setPickTarget(mState.action, mState.copyOperationSubType, cwd);
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700315 }
316 }
Steve McKayef3e2cf2015-04-20 17:18:15 -0700317 }
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700318
Steve McKayef3e2cf2015-04-20 17:18:15 -0700319 void onSaveRequested(DocumentInfo replaceTarget) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800320 new ExistingFinishTask(this, replaceTarget.derivedUri)
321 .executeOnExecutor(getExecutorForCurrentDirectory());
Steve McKayef3e2cf2015-04-20 17:18:15 -0700322 }
Jeff Sharkey28c05ee2013-09-06 13:22:09 -0700323
Steve McKayef3e2cf2015-04-20 17:18:15 -0700324 void onSaveRequested(String mimeType, String displayName) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800325 new CreateFinishTask(this, mimeType, displayName)
326 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700327 }
328
Steve McKaye934ce62015-03-25 14:35:33 -0700329 @Override
Steve McKayef3e2cf2015-04-20 17:18:15 -0700330 void onRootPicked(RootInfo root) {
331 super.onRootPicked(root);
Steve McKay1f264a82016-02-03 11:15:57 -0800332 mNavigator.revealRootsDrawer(false);
333 }
334
Steve McKaye934ce62015-03-25 14:35:33 -0700335 @Override
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +0900336 public void onDocumentPicked(DocumentInfo doc, Model model) {
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700337 final FragmentManager fm = getFragmentManager();
Tomasz Mikolajewski39acff52015-11-25 13:01:18 +0900338 if (doc.isContainer()) {
339 openContainerDocument(doc);
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700340 } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700341 // Explicit file picked, return
Steve McKay95cd85a2016-02-04 12:15:22 -0800342 new ExistingFinishTask(this, doc.derivedUri)
343 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700344 } else if (mState.action == ACTION_CREATE) {
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700345 // Replace selected file
346 SaveFragment.get(fm).setReplaceTarget(doc);
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700347 }
348 }
349
Steve McKaye934ce62015-03-25 14:35:33 -0700350 @Override
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700351 public void onDocumentsPicked(List<DocumentInfo> docs) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700352 if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey4eb407a2013-08-18 17:38:20 -0700353 final int size = docs.size();
354 final Uri[] uris = new Uri[size];
355 for (int i = 0; i < size; i++) {
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700356 uris[i] = docs.get(i).derivedUri;
Jeff Sharkey4eb407a2013-08-18 17:38:20 -0700357 }
Steve McKay95cd85a2016-02-04 12:15:22 -0800358 new ExistingFinishTask(this, uris)
359 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700360 }
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700361 }
362
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700363 public void onPickRequested(DocumentInfo pickTarget) {
Daichi Hironobbe22922015-04-10 15:50:38 +0900364 Uri result;
365 if (mState.action == ACTION_OPEN_TREE) {
366 result = DocumentsContract.buildTreeDocumentUri(
367 pickTarget.authority, pickTarget.documentId);
Ben Kwa84cebbe2015-09-25 14:48:29 -0700368 } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
Daichi Hironobbe22922015-04-10 15:50:38 +0900369 result = pickTarget.derivedUri;
370 } else {
371 // Should not be reached.
372 throw new IllegalStateException("Invalid mState.action.");
373 }
Steve McKay95cd85a2016-02-04 12:15:22 -0800374 new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700375 }
376
Steve McKay95cd85a2016-02-04 12:15:22 -0800377 void writeStackToRecentsBlocking() {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700378 final ContentResolver resolver = getContentResolver();
379 final ContentValues values = new ContentValues();
380
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700381 final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
Daichi Hironobbe22922015-04-10 15:50:38 +0900382 if (mState.action == ACTION_CREATE ||
383 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700384 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700385 // Remember stack for last create
386 values.clear();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700387 values.put(RecentColumns.KEY, mState.stack.buildKey());
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700388 values.put(RecentColumns.STACK, rawStack);
389 resolver.insert(RecentsProvider.buildRecent(), values);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700390 }
391
392 // Remember location for next app launch
Jeff Sharkey15be8362013-10-09 13:52:17 -0700393 final String packageName = getCallingPackageMaybeExtra();
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700394 values.clear();
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700395 values.put(ResumeColumns.STACK, rawStack);
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700396 values.put(ResumeColumns.EXTERNAL, 0);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700397 resolver.insert(RecentsProvider.buildResume(packageName), values);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700398 }
399
Steve McKayef3e2cf2015-04-20 17:18:15 -0700400 @Override
401 void onTaskFinished(Uri... uris) {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700402 Log.d(TAG, "onFinished() " + Arrays.toString(uris));
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700403
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700404 final Intent intent = new Intent();
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700405 if (uris.length == 1) {
406 intent.setData(uris[0]);
407 } else if (uris.length > 1) {
Jeff Sharkeyef7184a2013-08-05 17:56:48 -0700408 final ClipData clipData = new ClipData(
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700409 null, mState.acceptMimes, new ClipData.Item(uris[0]));
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700410 for (int i = 1; i < uris.length; i++) {
411 clipData.addItem(new ClipData.Item(uris[i]));
412 }
413 intent.setClipData(clipData);
414 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700415
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700416 if (mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700417 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Jeff Sharkey04ec6f42015-07-15 09:13:14 -0700418 } else if (mState.action == ACTION_OPEN_TREE) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700419 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
420 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
421 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
422 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
Ben Kwa84cebbe2015-09-25 14:48:29 -0700423 } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey04ec6f42015-07-15 09:13:14 -0700424 // Picking a copy destination is only used internally by us, so we
425 // don't need to extend permissions to the caller.
Steve McKay323ee3e2015-09-25 16:02:56 -0700426 intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
Steve McKaya6bbeab2016-02-17 15:02:01 -0800427 intent.putExtra(FileOperationService.EXTRA_OPERATION, mState.copyOperationSubType);
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700428 } else {
429 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
430 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
Jeff Sharkeye66c1772013-09-20 14:30:59 -0700431 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700432 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700433
434 setResult(Activity.RESULT_OK, intent);
435 finish();
436 }
437
Steve McKay95cd85a2016-02-04 12:15:22 -0800438
Steve McKayef3e2cf2015-04-20 17:18:15 -0700439 public static DocumentsActivity get(Fragment fragment) {
440 return (DocumentsActivity) fragment.getActivity();
441 }
442
Steve McKay95cd85a2016-02-04 12:15:22 -0800443 /**
444 * Restores the stack from Recents for the specified package.
445 */
446 private static final class RestoreStackTask
447 extends PairedTask<DocumentsActivity, Void, Void> {
448
449 private volatile boolean mRestoredStack;
450 private volatile boolean mExternal;
Steve McKay95cd85a2016-02-04 12:15:22 -0800451 private State mState;
452
453 public RestoreStackTask(DocumentsActivity activity) {
454 super(activity);
455 mState = activity.mState;
456 }
457
458 @Override
459 protected Void run(Void... params) {
460 if (DEBUG && !mState.stack.isEmpty()) {
461 Log.w(TAG, "Overwriting existing stack.");
462 }
Ben Kwaa4761f02016-02-05 06:44:15 -0800463 RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
Steve McKay95cd85a2016-02-04 12:15:22 -0800464
465 String packageName = mOwner.getCallingPackageMaybeExtra();
466 Uri resumeUri = RecentsProvider.buildResume(packageName);
467 Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null);
468 try {
469 if (cursor.moveToFirst()) {
470 mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
471 final byte[] rawStack = cursor.getBlob(
472 cursor.getColumnIndex(ResumeColumns.STACK));
473 DurableUtils.readFromArray(rawStack, mState.stack);
474 mRestoredStack = true;
475 }
476 } catch (IOException e) {
477 Log.w(TAG, "Failed to resume: " + e);
478 } finally {
479 IoUtils.closeQuietly(cursor);
480 }
481
482 if (mRestoredStack) {
483 // Update the restored stack to ensure we have freshest data
484 final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
485 try {
486 mState.stack.updateRoot(matchingRoots);
487 mState.stack.updateDocuments(mOwner.getContentResolver());
488 } catch (FileNotFoundException e) {
489 Log.w(TAG, "Failed to restore stack for package: " + packageName
490 + " because of error: "+ e);
491 mState.stack.reset();
492 mRestoredStack = false;
493 }
494 }
495
496 return null;
497 }
498
499 @Override
500 protected void finish(Void result) {
501 mState.restored = true;
502 mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
503 mOwner.onStackRestored(mRestoredStack, mExternal);
504 }
505 }
506
507 private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700508 private final Uri mUri;
509
Steve McKay95cd85a2016-02-04 12:15:22 -0800510 public PickFinishTask(DocumentsActivity activity, Uri uri) {
511 super(activity);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700512 mUri = uri;
513 }
514
515 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800516 protected Void run(Void... params) {
517 mOwner.writeStackToRecentsBlocking();
Steve McKayef3e2cf2015-04-20 17:18:15 -0700518 return null;
519 }
520
521 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800522 protected void finish(Void result) {
523 mOwner.onTaskFinished(mUri);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700524 }
525 }
526
Steve McKay95cd85a2016-02-04 12:15:22 -0800527 private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700528 private final Uri[] mUris;
529
Steve McKay95cd85a2016-02-04 12:15:22 -0800530 public ExistingFinishTask(DocumentsActivity activity, Uri... uris) {
531 super(activity);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700532 mUris = uris;
533 }
534
535 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800536 protected Void run(Void... params) {
537 mOwner.writeStackToRecentsBlocking();
Steve McKayef3e2cf2015-04-20 17:18:15 -0700538 return null;
539 }
540
541 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800542 protected void finish(Void result) {
543 mOwner.onTaskFinished(mUris);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700544 }
545 }
546
547 /**
548 * Task that creates a new document in the background.
549 */
Steve McKay95cd85a2016-02-04 12:15:22 -0800550 private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700551 private final String mMimeType;
552 private final String mDisplayName;
553
Steve McKay95cd85a2016-02-04 12:15:22 -0800554 public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) {
555 super(activity);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700556 mMimeType = mimeType;
557 mDisplayName = displayName;
558 }
559
560 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800561 protected void prepare() {
562 mOwner.setPending(true);
Jeff Sharkey04d45a02013-10-23 15:46:38 -0700563 }
564
565 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800566 protected Uri run(Void... params) {
567 final ContentResolver resolver = mOwner.getContentResolver();
568 final DocumentInfo cwd = mOwner.getCurrentDirectory();
Jeff Sharkey7aa76012013-09-30 14:26:27 -0700569
570 ContentProviderClient client = null;
571 Uri childUri = null;
572 try {
573 client = DocumentsApplication.acquireUnstableProviderOrThrow(
574 resolver, cwd.derivedUri.getAuthority());
575 childUri = DocumentsContract.createDocument(
576 client, cwd.derivedUri, mMimeType, mDisplayName);
577 } catch (Exception e) {
578 Log.w(TAG, "Failed to create document", e);
579 } finally {
580 ContentProviderClient.releaseQuietly(client);
581 }
582
Jeff Sharkey6efba222013-09-27 16:44:11 -0700583 if (childUri != null) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800584 mOwner.writeStackToRecentsBlocking();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700585 }
Jeff Sharkey7aa76012013-09-30 14:26:27 -0700586
Jeff Sharkey6efba222013-09-27 16:44:11 -0700587 return childUri;
588 }
589
590 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800591 protected void finish(Uri result) {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700592 if (result != null) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800593 mOwner.onTaskFinished(result);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700594 } else {
Steve McKay5bbae102015-10-01 11:39:24 -0700595 Snackbars.makeSnackbar(
Steve McKay95cd85a2016-02-04 12:15:22 -0800596 mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700597 }
Jeff Sharkey04d45a02013-10-23 15:46:38 -0700598
Steve McKay95cd85a2016-02-04 12:15:22 -0800599 mOwner.setPending(false);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700600 }
601 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700602}