blob: 6ecdc905a60be58dfdcfc47dd6b8cfec663fa8ec [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;
Steve McKayb9a20d12016-02-19 12:57:05 -080026import static com.android.internal.util.Preconditions.checkArgument;
Jeff Sharkey59d577a2015-04-11 21:27:21 -070027
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070028import android.app.Activity;
Steve McKayef3e2cf2015-04-20 17:18:15 -070029import android.app.Fragment;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070030import android.app.FragmentManager;
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -070031import android.content.ClipData;
Jeff Sharkey54ca29a2013-08-15 11:24:03 -070032import android.content.ComponentName;
Jeff Sharkey7aa76012013-09-30 14:26:27 -070033import android.content.ContentProviderClient;
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -070034import android.content.ContentResolver;
Jeff Sharkey20d96d82013-07-30 17:08:39 -070035import android.content.ContentValues;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070036import android.content.Intent;
Jeff Sharkey54ca29a2013-08-15 11:24:03 -070037import android.content.pm.ResolveInfo;
Steve McKay95cd85a2016-02-04 12:15:22 -080038import android.database.Cursor;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070039import android.net.Uri;
40import android.os.Bundle;
Tomasz Mikolajewskie1a03f82015-04-10 10:30:33 +090041import android.os.Parcelable;
Jeff Sharkeya5599ef2013-08-15 16:17:41 -070042import android.provider.DocumentsContract;
Ben Kwa94b486d2015-09-30 10:00:10 -070043import android.support.design.widget.Snackbar;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070044import android.util.Log;
Jeff Sharkey54e55b72013-06-30 20:02:59 -070045import android.view.Menu;
46import android.view.MenuItem;
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070047
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070048import com.android.documentsui.RecentsProvider.RecentColumns;
49import com.android.documentsui.RecentsProvider.ResumeColumns;
Steve McKayf8621552015-11-03 15:23:16 -080050import com.android.documentsui.dirlist.DirectoryFragment;
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +090051import com.android.documentsui.dirlist.Model;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070052import com.android.documentsui.model.DocumentInfo;
Jeff Sharkeyb5133112013-09-01 18:41:04 -070053import com.android.documentsui.model.DurableUtils;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070054import com.android.documentsui.model.RootInfo;
Steve McKay14e827a2016-01-06 18:32:13 -080055import com.android.documentsui.services.FileOperationService;
Jeff Sharkey20d96d82013-07-30 17:08:39 -070056
Steve McKay95cd85a2016-02-04 12:15:22 -080057import libcore.io.IoUtils;
58
59import java.io.FileNotFoundException;
60import java.io.IOException;
Ben Kwacc345212015-05-18 13:18:05 -070061import java.util.Arrays;
Steve McKay95cd85a2016-02-04 12:15:22 -080062import java.util.Collection;
Ben Kwacc345212015-05-18 13:18:05 -070063import java.util.List;
64
Steve McKaye934ce62015-03-25 14:35:33 -070065public class DocumentsActivity extends BaseActivity {
Steve McKayef3e2cf2015-04-20 17:18:15 -070066 private static final int CODE_FORWARD = 42;
Ben Kwa0f7078f02015-09-08 07:31:19 -070067 private static final String TAG = "DocumentsActivity";
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070068
Steve McKayef3e2cf2015-04-20 17:18:15 -070069 public DocumentsActivity() {
Steve McKay1f264a82016-02-03 11:15:57 -080070 super(R.layout.documents_activity, TAG);
Steve McKayef3e2cf2015-04-20 17:18:15 -070071 }
72
Jeff Sharkey9e0036e2013-04-26 16:54:55 -070073 @Override
74 public void onCreate(Bundle icicle) {
Steve McKay12055472015-08-20 16:48:49 -070075 super.onCreate(icicle);
Oren Blasberg9e9c2462015-06-25 19:00:43 -070076
Jeff Sharkeyb5133112013-09-01 18:41:04 -070077 if (mState.action == ACTION_CREATE) {
78 final String mimeType = getIntent().getType();
79 final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
80 SaveFragment.show(getFragmentManager(), mimeType, title);
Daichi Hironobbe22922015-04-10 15:50:38 +090081 } else if (mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -070082 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -070083 PickFragment.show(getFragmentManager());
Jeff Sharkeyb5133112013-09-01 18:41:04 -070084 }
85
86 if (mState.action == ACTION_GET_CONTENT) {
87 final Intent moreApps = new Intent(getIntent());
88 moreApps.setComponent(null);
89 moreApps.setPackage(null);
90 RootsFragment.show(getFragmentManager(), moreApps);
Daichi Hironobbe22922015-04-10 15:50:38 +090091 } else if (mState.action == ACTION_OPEN ||
92 mState.action == ACTION_CREATE ||
93 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -070094 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -070095 RootsFragment.show(getFragmentManager(), null);
96 }
97
Jeff Sharkeya9ce0492013-09-19 15:25:56 -070098 if (!mState.restored) {
Makoto Onuki9feac142015-06-23 14:29:25 -070099 // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
100 // talkback from reading aloud the default title, we clear it here.
101 setTitle("");
Steve McKay95cd85a2016-02-04 12:15:22 -0800102 new RestoreStackTask(this).execute();
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700103 } else {
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800104 refreshCurrentRootAndDirectory(ANIM_NONE);
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700105 }
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700106 }
107
Steve McKay12055472015-08-20 16:48:49 -0700108 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800109 void includeState(State state) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700110 final Intent intent = getIntent();
111 final String action = intent.getAction();
112 if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700113 state.action = ACTION_OPEN;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700114 } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700115 state.action = ACTION_CREATE;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700116 } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700117 state.action = ACTION_GET_CONTENT;
Jeff Sharkeyb9fbb722014-06-04 16:42:47 -0700118 } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700119 state.action = ACTION_OPEN_TREE;
Ben Kwa84cebbe2015-09-25 14:48:29 -0700120 } else if (Shared.ACTION_PICK_COPY_DESTINATION.equals(action)) {
121 state.action = ACTION_PICK_COPY_DESTINATION;
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700122 }
123
Steve McKayef3e2cf2015-04-20 17:18:15 -0700124 if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
125 state.allowMultiple = intent.getBooleanExtra(
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700126 Intent.EXTRA_ALLOW_MULTIPLE, false);
127 }
128
Tomasz Mikolajewskia8057a92015-11-16 11:41:28 +0900129 if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT
130 || state.action == ACTION_CREATE) {
131 state.openableOnly = intent.hasCategory(Intent.CATEGORY_OPENABLE);
132 }
133
Ben Kwa84cebbe2015-09-25 14:48:29 -0700134 if (state.action == ACTION_PICK_COPY_DESTINATION) {
Steve McKaya6bbeab2016-02-17 15:02:01 -0800135 // Indicates that a copy operation (or move) includes a directory.
136 // Why? Directory creation isn't supported by some roots (like Downloads).
137 // This allows us to restrict available roots to just those with support.
Steve McKayef3e2cf2015-04-20 17:18:15 -0700138 state.directoryCopy = intent.getBooleanExtra(
Ben Kwa84cebbe2015-09-25 14:48:29 -0700139 Shared.EXTRA_DIRECTORY_COPY, false);
Steve McKaya6bbeab2016-02-17 15:02:01 -0800140 state.copyOperationSubType = intent.getIntExtra(
141 FileOperationService.EXTRA_OPERATION,
Steve McKay14e827a2016-01-06 18:32:13 -0800142 FileOperationService.OPERATION_COPY);
Daichi Hirono9be34292015-04-14 17:12:54 +0900143 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700144 }
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700145
Steve McKayaa15dae2016-02-09 16:17:24 -0800146 private void onStackRestored(boolean restored, boolean external) {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700147 // Show drawer when no stack restored, but only when requesting
148 // non-visual content. However, if we last used an external app,
149 // drawer is always shown.
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700150
Steve McKayef3e2cf2015-04-20 17:18:15 -0700151 boolean showDrawer = false;
152 if (!restored) {
153 showDrawer = true;
154 }
155 if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
156 showDrawer = false;
157 }
158 if (external && mState.action == ACTION_GET_CONTENT) {
159 showDrawer = true;
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700160 }
Steve McKaya6bbeab2016-02-17 15:02:01 -0800161 if (mState.action == ACTION_PICK_COPY_DESTINATION) {
162 showDrawer = true;
163 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700164
Steve McKayef3e2cf2015-04-20 17:18:15 -0700165 if (showDrawer) {
Steve McKay1f264a82016-02-03 11:15:57 -0800166 mNavigator.revealRootsDrawer(true);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700167 }
168 }
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700169
Steve McKayef3e2cf2015-04-20 17:18:15 -0700170 public void onAppPicked(ResolveInfo info) {
171 final Intent intent = new Intent(getIntent());
172 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
173 intent.setComponent(new ComponentName(
174 info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
175 startActivityForResult(intent, CODE_FORWARD);
176 }
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700177
Steve McKayef3e2cf2015-04-20 17:18:15 -0700178 @Override
179 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
180 Log.d(TAG, "onActivityResult() code=" + resultCode);
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700181
Steve McKayef3e2cf2015-04-20 17:18:15 -0700182 // Only relay back results when not canceled; otherwise stick around to
183 // let the user pick another app/backend.
184 if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
Jeff Sharkeya9ce0492013-09-19 15:25:56 -0700185
Steve McKayef3e2cf2015-04-20 17:18:15 -0700186 // Remember that we last picked via external app
187 final String packageName = getCallingPackageMaybeExtra();
188 final ContentValues values = new ContentValues();
189 values.put(ResumeColumns.EXTERNAL, 1);
190 getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
191
192 // Pass back result to original caller
193 setResult(resultCode, data);
194 finish();
195 } else {
196 super.onActivityResult(requestCode, resultCode, data);
Jeff Sharkeya5599ef2013-08-15 16:17:41 -0700197 }
198 }
199
Jeff Sharkeybc2971d2013-07-31 20:53:22 -0700200 @Override
201 protected void onPostCreate(Bundle savedInstanceState) {
202 super.onPostCreate(savedInstanceState);
Steve McKay1f264a82016-02-03 11:15:57 -0800203 mDrawer.update();
204 mNavigator.update();
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700205 }
206
Steve McKayef3e2cf2015-04-20 17:18:15 -0700207 @Override
Steve McKay1f264a82016-02-03 11:15:57 -0800208 public String getDrawerTitle() {
209 String title = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
210 if (title == null) {
211 if (mState.action == ACTION_OPEN ||
212 mState.action == ACTION_GET_CONTENT ||
213 mState.action == ACTION_OPEN_TREE) {
214 title = getResources().getString(R.string.title_open);
215 } else if (mState.action == ACTION_CREATE ||
216 mState.action == ACTION_PICK_COPY_DESTINATION) {
217 title = getResources().getString(R.string.title_save);
Tomasz Mikolajewski0e591f92015-06-12 16:22:17 -0700218 } else {
Steve McKay1f264a82016-02-03 11:15:57 -0800219 // If all else fails, just call it "Files".
220 title = getResources().getString(R.string.files_label);
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700221 }
Jeff Sharkeyf52773f2014-07-28 16:38:52 -0700222 }
Jeff Sharkeyef7184a2013-08-05 17:56:48 -0700223
Steve McKay1f264a82016-02-03 11:15:57 -0800224 return title;
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700225 }
226
227 @Override
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700228 public boolean onPrepareOptionsMenu(Menu menu) {
229 super.onPrepareOptionsMenu(menu);
230
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700231 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700232
Steve McKay3ce95952016-02-02 11:41:03 -0800233 boolean picking = mState.action == ACTION_CREATE
234 || mState.action == ACTION_OPEN_TREE
235 || mState.action == ACTION_PICK_COPY_DESTINATION;
236
237 if (picking) {
238 // May already be hidden because the root
239 // doesn't support search.
240 mSearchManager.showMenu(false);
241 }
242
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700243 final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
Jeff Sharkeyded77182013-09-03 14:17:06 -0700244 final MenuItem grid = menu.findItem(R.id.menu_grid);
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700245 final MenuItem list = menu.findItem(R.id.menu_list);
Jeff Sharkey669f8e72014-08-08 15:10:03 -0700246 final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700247
Steve McKay5bbae102015-10-01 11:39:24 -0700248 boolean recents = cwd == null;
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700249
Steve McKay5bbae102015-10-01 11:39:24 -0700250 createDir.setVisible(picking && !recents && cwd.isCreateSupported());
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700251
Steve McKay5bbae102015-10-01 11:39:24 -0700252 // No display options in recent directories
Steve McKay7c7e6842016-01-08 14:58:33 -0800253 if (picking && recents) {
254 grid.setVisible(false);
255 list.setVisible(false);
256 }
Steve McKay5bbae102015-10-01 11:39:24 -0700257
258 fileSize.setVisible(fileSize.isVisible() && !picking);
Steve McKay5bbae102015-10-01 11:39:24 -0700259
260 if (mState.action == ACTION_CREATE) {
261 final FragmentManager fm = getFragmentManager();
Steve McKay0c2a0a82015-10-14 17:41:13 -0700262 SaveFragment.get(fm).prepareForDirectory(cwd);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700263 }
Jeff Sharkeybc2971d2013-07-31 20:53:22 -0700264
Steve McKay5bbae102015-10-01 11:39:24 -0700265 Menus.disableHiddenItems(menu);
Jeff Sharkey1407d4c2015-04-12 21:52:24 -0700266
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700267 return true;
268 }
269
270 @Override
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800271 void refreshDirectory(int anim) {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700272 final FragmentManager fm = getFragmentManager();
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700273 final RootInfo root = getCurrentRoot();
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700274 final DocumentInfo cwd = getCurrentDirectory();
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700275
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700276 if (cwd == null) {
277 // No directory means recents
Daichi Hironobbe22922015-04-10 15:50:38 +0900278 if (mState.action == ACTION_CREATE ||
279 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700280 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700281 RecentsCreateFragment.show(fm);
282 } else {
Jeff Sharkeye20a3ac2013-09-18 16:26:49 -0700283 DirectoryFragment.showRecentsOpen(fm, anim);
Jeff Sharkey88f322c2013-09-12 16:25:02 -0700284
Steve McKay3eb2d072016-01-25 19:00:22 -0800285 // In recents we pick layout mode based on the mimetype,
286 // picking GRID for visual types. We intentionally don't
287 // consult a user's saved preferences here since they are
288 // set per root (not per root and per mimetype).
289 boolean visualMimes = MimePredicate.mimeMatches(
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700290 MimePredicate.VISUAL_MIMES, mState.acceptMimes);
Steve McKay3eb2d072016-01-25 19:00:22 -0800291 mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700292 }
293 } else {
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700294 // Normal boring directory
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800295 DirectoryFragment.showDirectory(fm, root, cwd, anim);
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700296 }
Jeff Sharkeyb156f4b2013-08-06 16:26:14 -0700297
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700298 // Forget any replacement target
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700299 if (mState.action == ACTION_CREATE) {
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700300 final SaveFragment save = SaveFragment.get(fm);
301 if (save != null) {
302 save.setReplaceTarget(null);
303 }
304 }
305
Daichi Hironobbe22922015-04-10 15:50:38 +0900306 if (mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700307 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700308 final PickFragment pick = PickFragment.get(fm);
309 if (pick != null) {
Steve McKaya6bbeab2016-02-17 15:02:01 -0800310 pick.setPickTarget(mState.action, mState.copyOperationSubType, cwd);
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700311 }
312 }
Steve McKayef3e2cf2015-04-20 17:18:15 -0700313 }
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700314
Steve McKayef3e2cf2015-04-20 17:18:15 -0700315 void onSaveRequested(DocumentInfo replaceTarget) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800316 new ExistingFinishTask(this, replaceTarget.derivedUri)
317 .executeOnExecutor(getExecutorForCurrentDirectory());
Steve McKayef3e2cf2015-04-20 17:18:15 -0700318 }
Jeff Sharkey28c05ee2013-09-06 13:22:09 -0700319
Steve McKayb9a20d12016-02-19 12:57:05 -0800320 @Override
321 void onDirectoryCreated(DocumentInfo doc) {
322 checkArgument(doc.isDirectory());
323 openContainerDocument(doc);
324 }
325
Steve McKayef3e2cf2015-04-20 17:18:15 -0700326 void onSaveRequested(String mimeType, String displayName) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800327 new CreateFinishTask(this, mimeType, displayName)
328 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700329 }
330
Steve McKaye934ce62015-03-25 14:35:33 -0700331 @Override
Steve McKayef3e2cf2015-04-20 17:18:15 -0700332 void onRootPicked(RootInfo root) {
333 super.onRootPicked(root);
Steve McKay1f264a82016-02-03 11:15:57 -0800334 mNavigator.revealRootsDrawer(false);
335 }
336
Steve McKaye934ce62015-03-25 14:35:33 -0700337 @Override
Tomasz Mikolajewski3d988a92016-02-16 12:28:43 +0900338 public void onDocumentPicked(DocumentInfo doc, Model model) {
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700339 final FragmentManager fm = getFragmentManager();
Tomasz Mikolajewski39acff52015-11-25 13:01:18 +0900340 if (doc.isContainer()) {
341 openContainerDocument(doc);
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700342 } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700343 // Explicit file picked, return
Steve McKay95cd85a2016-02-04 12:15:22 -0800344 new ExistingFinishTask(this, doc.derivedUri)
345 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700346 } else if (mState.action == ACTION_CREATE) {
Jeff Sharkey9fb567b2013-08-07 16:22:02 -0700347 // Replace selected file
348 SaveFragment.get(fm).setReplaceTarget(doc);
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700349 }
350 }
351
Steve McKaye934ce62015-03-25 14:35:33 -0700352 @Override
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700353 public void onDocumentsPicked(List<DocumentInfo> docs) {
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700354 if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey4eb407a2013-08-18 17:38:20 -0700355 final int size = docs.size();
356 final Uri[] uris = new Uri[size];
357 for (int i = 0; i < size; i++) {
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700358 uris[i] = docs.get(i).derivedUri;
Jeff Sharkey4eb407a2013-08-18 17:38:20 -0700359 }
Steve McKay95cd85a2016-02-04 12:15:22 -0800360 new ExistingFinishTask(this, uris)
361 .executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700362 }
Jeff Sharkey54e55b72013-06-30 20:02:59 -0700363 }
364
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700365 public void onPickRequested(DocumentInfo pickTarget) {
Daichi Hironobbe22922015-04-10 15:50:38 +0900366 Uri result;
367 if (mState.action == ACTION_OPEN_TREE) {
368 result = DocumentsContract.buildTreeDocumentUri(
369 pickTarget.authority, pickTarget.documentId);
Ben Kwa84cebbe2015-09-25 14:48:29 -0700370 } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
Daichi Hironobbe22922015-04-10 15:50:38 +0900371 result = pickTarget.derivedUri;
372 } else {
373 // Should not be reached.
374 throw new IllegalStateException("Invalid mState.action.");
375 }
Steve McKay95cd85a2016-02-04 12:15:22 -0800376 new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory());
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700377 }
378
Steve McKay95cd85a2016-02-04 12:15:22 -0800379 void writeStackToRecentsBlocking() {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700380 final ContentResolver resolver = getContentResolver();
381 final ContentValues values = new ContentValues();
382
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700383 final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
Daichi Hironobbe22922015-04-10 15:50:38 +0900384 if (mState.action == ACTION_CREATE ||
385 mState.action == ACTION_OPEN_TREE ||
Ben Kwa84cebbe2015-09-25 14:48:29 -0700386 mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700387 // Remember stack for last create
388 values.clear();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700389 values.put(RecentColumns.KEY, mState.stack.buildKey());
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700390 values.put(RecentColumns.STACK, rawStack);
391 resolver.insert(RecentsProvider.buildRecent(), values);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700392 }
393
394 // Remember location for next app launch
Jeff Sharkey15be8362013-10-09 13:52:17 -0700395 final String packageName = getCallingPackageMaybeExtra();
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700396 values.clear();
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700397 values.put(ResumeColumns.STACK, rawStack);
Jeff Sharkeydeffade2013-09-24 12:07:12 -0700398 values.put(ResumeColumns.EXTERNAL, 0);
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700399 resolver.insert(RecentsProvider.buildResume(packageName), values);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700400 }
401
Steve McKayef3e2cf2015-04-20 17:18:15 -0700402 @Override
403 void onTaskFinished(Uri... uris) {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700404 Log.d(TAG, "onFinished() " + Arrays.toString(uris));
Jeff Sharkeydc2963a2013-08-02 15:55:26 -0700405
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700406 final Intent intent = new Intent();
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700407 if (uris.length == 1) {
408 intent.setData(uris[0]);
409 } else if (uris.length > 1) {
Jeff Sharkeyef7184a2013-08-05 17:56:48 -0700410 final ClipData clipData = new ClipData(
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700411 null, mState.acceptMimes, new ClipData.Item(uris[0]));
Jeff Sharkeybe8b12e2013-07-01 16:56:54 -0700412 for (int i = 1; i < uris.length; i++) {
413 clipData.addItem(new ClipData.Item(uris[i]));
414 }
415 intent.setClipData(clipData);
416 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700417
Jeff Sharkeyb5133112013-09-01 18:41:04 -0700418 if (mState.action == ACTION_GET_CONTENT) {
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700419 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Jeff Sharkey04ec6f42015-07-15 09:13:14 -0700420 } else if (mState.action == ACTION_OPEN_TREE) {
Jeff Sharkey21de56a2014-04-05 19:05:24 -0700421 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
422 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
423 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
424 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
Ben Kwa84cebbe2015-09-25 14:48:29 -0700425 } else if (mState.action == ACTION_PICK_COPY_DESTINATION) {
Jeff Sharkey04ec6f42015-07-15 09:13:14 -0700426 // Picking a copy destination is only used internally by us, so we
427 // don't need to extend permissions to the caller.
Steve McKay323ee3e2015-09-25 16:02:56 -0700428 intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
Steve McKaya6bbeab2016-02-17 15:02:01 -0800429 intent.putExtra(FileOperationService.EXTRA_OPERATION, mState.copyOperationSubType);
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700430 } else {
431 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
432 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
Jeff Sharkeye66c1772013-09-20 14:30:59 -0700433 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
Jeff Sharkey54ca29a2013-08-15 11:24:03 -0700434 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700435
436 setResult(Activity.RESULT_OK, intent);
437 finish();
438 }
439
Steve McKay95cd85a2016-02-04 12:15:22 -0800440
Steve McKayef3e2cf2015-04-20 17:18:15 -0700441 public static DocumentsActivity get(Fragment fragment) {
442 return (DocumentsActivity) fragment.getActivity();
443 }
444
Steve McKay95cd85a2016-02-04 12:15:22 -0800445 /**
446 * Restores the stack from Recents for the specified package.
447 */
448 private static final class RestoreStackTask
449 extends PairedTask<DocumentsActivity, Void, Void> {
450
451 private volatile boolean mRestoredStack;
452 private volatile boolean mExternal;
Steve McKay95cd85a2016-02-04 12:15:22 -0800453 private State mState;
454
455 public RestoreStackTask(DocumentsActivity activity) {
456 super(activity);
457 mState = activity.mState;
458 }
459
460 @Override
461 protected Void run(Void... params) {
462 if (DEBUG && !mState.stack.isEmpty()) {
463 Log.w(TAG, "Overwriting existing stack.");
464 }
Ben Kwaa4761f02016-02-05 06:44:15 -0800465 RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
Steve McKay95cd85a2016-02-04 12:15:22 -0800466
467 String packageName = mOwner.getCallingPackageMaybeExtra();
468 Uri resumeUri = RecentsProvider.buildResume(packageName);
469 Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null);
470 try {
471 if (cursor.moveToFirst()) {
472 mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
473 final byte[] rawStack = cursor.getBlob(
474 cursor.getColumnIndex(ResumeColumns.STACK));
475 DurableUtils.readFromArray(rawStack, mState.stack);
476 mRestoredStack = true;
477 }
478 } catch (IOException e) {
479 Log.w(TAG, "Failed to resume: " + e);
480 } finally {
481 IoUtils.closeQuietly(cursor);
482 }
483
484 if (mRestoredStack) {
485 // Update the restored stack to ensure we have freshest data
486 final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
487 try {
488 mState.stack.updateRoot(matchingRoots);
489 mState.stack.updateDocuments(mOwner.getContentResolver());
490 } catch (FileNotFoundException e) {
491 Log.w(TAG, "Failed to restore stack for package: " + packageName
492 + " because of error: "+ e);
493 mState.stack.reset();
494 mRestoredStack = false;
495 }
496 }
497
498 return null;
499 }
500
501 @Override
502 protected void finish(Void result) {
503 mState.restored = true;
504 mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
505 mOwner.onStackRestored(mRestoredStack, mExternal);
506 }
507 }
508
509 private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700510 private final Uri mUri;
511
Steve McKay95cd85a2016-02-04 12:15:22 -0800512 public PickFinishTask(DocumentsActivity activity, Uri uri) {
513 super(activity);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700514 mUri = uri;
515 }
516
517 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800518 protected Void run(Void... params) {
519 mOwner.writeStackToRecentsBlocking();
Steve McKayef3e2cf2015-04-20 17:18:15 -0700520 return null;
521 }
522
523 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800524 protected void finish(Void result) {
525 mOwner.onTaskFinished(mUri);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700526 }
527 }
528
Steve McKay95cd85a2016-02-04 12:15:22 -0800529 private static final class ExistingFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
Steve McKayef3e2cf2015-04-20 17:18:15 -0700530 private final Uri[] mUris;
531
Steve McKay95cd85a2016-02-04 12:15:22 -0800532 public ExistingFinishTask(DocumentsActivity activity, Uri... uris) {
533 super(activity);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700534 mUris = uris;
535 }
536
537 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800538 protected Void run(Void... params) {
539 mOwner.writeStackToRecentsBlocking();
Steve McKayef3e2cf2015-04-20 17:18:15 -0700540 return null;
541 }
542
543 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800544 protected void finish(Void result) {
545 mOwner.onTaskFinished(mUris);
Steve McKayef3e2cf2015-04-20 17:18:15 -0700546 }
547 }
548
549 /**
550 * Task that creates a new document in the background.
551 */
Steve McKay95cd85a2016-02-04 12:15:22 -0800552 private static final class CreateFinishTask extends PairedTask<DocumentsActivity, Void, Uri> {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700553 private final String mMimeType;
554 private final String mDisplayName;
555
Steve McKay95cd85a2016-02-04 12:15:22 -0800556 public CreateFinishTask(DocumentsActivity activity, String mimeType, String displayName) {
557 super(activity);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700558 mMimeType = mimeType;
559 mDisplayName = displayName;
560 }
561
562 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800563 protected void prepare() {
564 mOwner.setPending(true);
Jeff Sharkey04d45a02013-10-23 15:46:38 -0700565 }
566
567 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800568 protected Uri run(Void... params) {
569 final ContentResolver resolver = mOwner.getContentResolver();
570 final DocumentInfo cwd = mOwner.getCurrentDirectory();
Jeff Sharkey7aa76012013-09-30 14:26:27 -0700571
572 ContentProviderClient client = null;
573 Uri childUri = null;
574 try {
575 client = DocumentsApplication.acquireUnstableProviderOrThrow(
576 resolver, cwd.derivedUri.getAuthority());
577 childUri = DocumentsContract.createDocument(
578 client, cwd.derivedUri, mMimeType, mDisplayName);
579 } catch (Exception e) {
580 Log.w(TAG, "Failed to create document", e);
581 } finally {
582 ContentProviderClient.releaseQuietly(client);
583 }
584
Jeff Sharkey6efba222013-09-27 16:44:11 -0700585 if (childUri != null) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800586 mOwner.writeStackToRecentsBlocking();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700587 }
Jeff Sharkey7aa76012013-09-30 14:26:27 -0700588
Jeff Sharkey6efba222013-09-27 16:44:11 -0700589 return childUri;
590 }
591
592 @Override
Steve McKay95cd85a2016-02-04 12:15:22 -0800593 protected void finish(Uri result) {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700594 if (result != null) {
Steve McKay95cd85a2016-02-04 12:15:22 -0800595 mOwner.onTaskFinished(result);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700596 } else {
Steve McKay5bbae102015-10-01 11:39:24 -0700597 Snackbars.makeSnackbar(
Steve McKay95cd85a2016-02-04 12:15:22 -0800598 mOwner, R.string.save_error, Snackbar.LENGTH_SHORT).show();
Jeff Sharkey6efba222013-09-27 16:44:11 -0700599 }
Jeff Sharkey04d45a02013-10-23 15:46:38 -0700600
Steve McKay95cd85a2016-02-04 12:15:22 -0800601 mOwner.setPending(false);
Jeff Sharkey6efba222013-09-27 16:44:11 -0700602 }
603 }
Jeff Sharkey9e0036e2013-04-26 16:54:55 -0700604}