Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
Steve McKay | b6006b2 | 2016-09-29 09:23:45 -0700 | [diff] [blame] | 17 | package com.android.documentsui.files; |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 18 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 19 | import static com.android.documentsui.base.Shared.DEBUG; |
| 20 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 21 | import android.app.Activity; |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 22 | import android.content.ActivityNotFoundException; |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 23 | import android.content.ClipData; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 24 | import android.content.ContentProviderClient; |
| 25 | import android.content.ContentResolver; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 26 | import android.content.Intent; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 27 | import android.net.Uri; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 28 | import android.provider.DocumentsContract; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 29 | import android.util.Log; |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 30 | import android.view.DragEvent; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 31 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 32 | import com.android.documentsui.AbstractActionHandler; |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 33 | import com.android.documentsui.ActionModeAddons; |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 34 | import com.android.documentsui.ActivityConfig; |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 35 | import com.android.documentsui.DocumentsAccess; |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 36 | import com.android.documentsui.DocumentsApplication; |
Garfield Tan | da2c0f0 | 2017-04-11 13:47:58 -0700 | [diff] [blame] | 37 | import com.android.documentsui.DragAndDropManager; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 38 | import com.android.documentsui.Injector; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 39 | import com.android.documentsui.Metrics; |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 40 | import com.android.documentsui.Model; |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 41 | import com.android.documentsui.R; |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 42 | import com.android.documentsui.TimeoutTask; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 43 | import com.android.documentsui.base.ConfirmationCallback; |
| 44 | import com.android.documentsui.base.ConfirmationCallback.Result; |
Steve McKay | 84440eb | 2017-06-23 12:35:15 -0700 | [diff] [blame] | 45 | import com.android.documentsui.base.DebugFlags; |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 46 | import com.android.documentsui.base.DocumentFilters; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 47 | import com.android.documentsui.base.DocumentInfo; |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 48 | import com.android.documentsui.base.DocumentStack; |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 49 | import com.android.documentsui.base.Features; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 50 | import com.android.documentsui.base.Lookup; |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 51 | import com.android.documentsui.base.MimeTypes; |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 52 | import com.android.documentsui.base.RootInfo; |
Ben Lin | df0fe89 | 2016-10-18 17:36:05 -0700 | [diff] [blame] | 53 | import com.android.documentsui.base.Shared; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 54 | import com.android.documentsui.base.State; |
| 55 | import com.android.documentsui.clipping.ClipStore; |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 56 | import com.android.documentsui.clipping.DocumentClipper; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 57 | import com.android.documentsui.clipping.UrisSupplier; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 58 | import com.android.documentsui.dirlist.AnimationView; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 59 | import com.android.documentsui.dirlist.DocumentDetails; |
Steve McKay | b6006b2 | 2016-09-29 09:23:45 -0700 | [diff] [blame] | 60 | import com.android.documentsui.files.ActionHandler.Addons; |
Steve McKay | f433d20 | 2017-07-12 18:46:09 -0700 | [diff] [blame^] | 61 | import com.android.documentsui.inspector.InspectorActivity; |
Steve McKay | 3a26823 | 2016-10-19 11:15:47 -0700 | [diff] [blame] | 62 | import com.android.documentsui.queries.SearchViewManager; |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 63 | import com.android.documentsui.roots.ProvidersAccess; |
Steve McKay | 4f78ba6 | 2016-10-04 16:48:49 -0700 | [diff] [blame] | 64 | import com.android.documentsui.selection.Selection; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 65 | import com.android.documentsui.services.FileOperation; |
| 66 | import com.android.documentsui.services.FileOperationService; |
| 67 | import com.android.documentsui.services.FileOperations; |
| 68 | import com.android.documentsui.ui.DialogController; |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 69 | import com.android.internal.annotations.VisibleForTesting; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 70 | |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 71 | import java.util.ArrayList; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 72 | import java.util.List; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 73 | import java.util.concurrent.Executor; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 74 | |
| 75 | import javax.annotation.Nullable; |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 76 | |
| 77 | /** |
Steve McKay | b6006b2 | 2016-09-29 09:23:45 -0700 | [diff] [blame] | 78 | * Provides {@link FilesActivity} action specializations to fragments. |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 79 | */ |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 80 | public class ActionHandler<T extends Activity & Addons> extends AbstractActionHandler<T> { |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 81 | |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 82 | private static final String TAG = "ManagerActionHandler"; |
| 83 | |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 84 | private final ActionModeAddons mActionModeAddons; |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 85 | private final Features mFeatures; |
| 86 | private final ActivityConfig mConfig; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 87 | private final DialogController mDialogs; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 88 | private final DocumentClipper mClipper; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 89 | private final ClipStore mClipStore; |
Garfield Tan | da2c0f0 | 2017-04-11 13:47:58 -0700 | [diff] [blame] | 90 | private final DragAndDropManager mDragAndDropManager; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 91 | private final Model mModel; |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 92 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 93 | ActionHandler( |
| 94 | T activity, |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 95 | State state, |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 96 | ProvidersAccess providers, |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 97 | DocumentsAccess docs, |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 98 | SearchViewManager searchMgr, |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 99 | Lookup<String, Executor> executors, |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 100 | ActionModeAddons actionModeAddons, |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 101 | DocumentClipper clipper, |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 102 | ClipStore clipStore, |
Garfield Tan | da2c0f0 | 2017-04-11 13:47:58 -0700 | [diff] [blame] | 103 | DragAndDropManager dragAndDropManager, |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 104 | Injector injector) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 105 | |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 106 | super(activity, state, providers, docs, searchMgr, executors, injector); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 107 | |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 108 | mActionModeAddons = actionModeAddons; |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 109 | mFeatures = injector.features; |
| 110 | mConfig = injector.config; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 111 | mDialogs = injector.dialogs; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 112 | mClipper = clipper; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 113 | mClipStore = clipStore; |
Garfield Tan | da2c0f0 | 2017-04-11 13:47:58 -0700 | [diff] [blame] | 114 | mDragAndDropManager = dragAndDropManager; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 115 | mModel = injector.getModel(); |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | @Override |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 119 | public boolean dropOn(DragEvent event, RootInfo root) { |
Ben Lin | d6a85b9 | 2017-03-13 16:26:26 -0700 | [diff] [blame] | 120 | if (!root.supportsCreate() || root.isLibrary()) { |
| 121 | return false; |
| 122 | } |
| 123 | |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 124 | // DragEvent gets recycled, so it is possible that by the time the callback is called, |
| 125 | // event.getLocalState() and event.getClipData() returns null. Thus, we want to save |
| 126 | // references to ensure they are non null. |
| 127 | final ClipData clipData = event.getClipData(); |
| 128 | final Object localState = event.getLocalState(); |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 129 | |
Garfield Tan | da2c0f0 | 2017-04-11 13:47:58 -0700 | [diff] [blame] | 130 | return mDragAndDropManager.drop( |
| 131 | clipData, localState, root, this, mDialogs::showFileOperationStatus); |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 132 | } |
| 133 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 134 | @Override |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 135 | public void openSelectedInNewWindow() { |
| 136 | Selection selection = getStableSelection(); |
| 137 | assert(selection.size() == 1); |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 138 | DocumentInfo doc = mModel.getDocument(selection.iterator().next()); |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 139 | assert(doc != null); |
| 140 | openInNewWindow(new DocumentStack(mState.stack, doc)); |
| 141 | } |
| 142 | |
| 143 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 144 | public void openSettings(RootInfo root) { |
| 145 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_SETTINGS); |
| 146 | final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS); |
| 147 | intent.setDataAndType(root.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM); |
| 148 | mActivity.startActivity(intent); |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | @Override |
| 152 | public void pasteIntoFolder(RootInfo root) { |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 153 | this.getRootDocument( |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 154 | root, |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 155 | TimeoutTask.DEFAULT_TIMEOUT, |
| 156 | (DocumentInfo doc) -> pasteIntoFolder(root, doc)); |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 157 | } |
| 158 | |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 159 | private void pasteIntoFolder(RootInfo root, @Nullable DocumentInfo doc) { |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 160 | DocumentStack stack = new DocumentStack(root, doc); |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 161 | mClipper.copyFromClipboard(doc, stack, mDialogs::showFileOperationStatus); |
Garfield Tan | b285b40 | 2016-09-21 17:12:18 -0700 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | @Override |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 165 | public @Nullable DocumentInfo renameDocument(String name, DocumentInfo document) { |
| 166 | ContentResolver resolver = mActivity.getContentResolver(); |
| 167 | ContentProviderClient client = null; |
| 168 | |
| 169 | try { |
| 170 | client = DocumentsApplication.acquireUnstableProviderOrThrow( |
| 171 | resolver, document.derivedUri.getAuthority()); |
| 172 | Uri newUri = DocumentsContract.renameDocument( |
| 173 | client, document.derivedUri, name); |
| 174 | return DocumentInfo.fromUri(resolver, newUri); |
| 175 | } catch (Exception e) { |
| 176 | Log.w(TAG, "Failed to rename file", e); |
| 177 | return null; |
| 178 | } finally { |
| 179 | ContentProviderClient.releaseQuietly(client); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 184 | public void openRoot(RootInfo root) { |
Garfield Tan | 2a8719c | 2017-01-19 16:17:04 -0800 | [diff] [blame] | 185 | Metrics.logRootVisited(mActivity, Metrics.FILES_SCOPE, root); |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 186 | mActivity.onRootPicked(root); |
| 187 | } |
| 188 | |
| 189 | @Override |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 190 | public boolean openDocument(DocumentDetails details, @ViewType int type, |
| 191 | @ViewType int fallback) { |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 192 | DocumentInfo doc = mModel.getDocument(details.getModelId()); |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 193 | if (doc == null) { |
| 194 | Log.w(TAG, |
| 195 | "Can't view item. No Document available for modeId: " + details.getModelId()); |
| 196 | return false; |
| 197 | } |
| 198 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 199 | return openDocument(doc, type, fallback); |
| 200 | } |
| 201 | |
| 202 | // TODO: Make this private and make tests call openDocument(DocumentDetails, int, int) instead. |
| 203 | @VisibleForTesting |
| 204 | public boolean openDocument(DocumentInfo doc, @ViewType int type, @ViewType int fallback) { |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 205 | if (mConfig.isDocumentEnabled(doc.mimeType, doc.flags, mState)) { |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 206 | onDocumentPicked(doc, type, fallback); |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 207 | mSelectionMgr.clearSelection(); |
Steve McKay | 6d20d19 | 2016-09-21 17:57:10 -0700 | [diff] [blame] | 208 | return true; |
| 209 | } |
| 210 | return false; |
| 211 | } |
| 212 | |
| 213 | @Override |
Ben Lin | d8d0ad2 | 2017-01-11 13:30:50 -0800 | [diff] [blame] | 214 | public void springOpenDirectory(DocumentInfo doc) { |
| 215 | assert(doc.isDirectory()); |
| 216 | mActionModeAddons.finishActionMode(); |
| 217 | openContainerDocument(doc); |
| 218 | } |
| 219 | |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 220 | private Selection getSelectedOrFocused() { |
| 221 | final Selection selection = this.getStableSelection(); |
| 222 | if (selection.isEmpty()) { |
| 223 | String focusModelId = mFocusHandler.getFocusModelId(); |
| 224 | if (focusModelId != null) { |
| 225 | selection.add(focusModelId); |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | return selection; |
| 230 | } |
| 231 | |
| 232 | @Override |
| 233 | public void cutToClipboard() { |
| 234 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_CUT_CLIPBOARD); |
| 235 | Selection selection = getSelectedOrFocused(); |
| 236 | |
| 237 | if (selection.isEmpty()) { |
| 238 | return; |
| 239 | } |
Ben Lin | c1a32ae | 2017-04-19 15:19:49 -0700 | [diff] [blame] | 240 | |
| 241 | if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) { |
| 242 | mDialogs.showOperationUnsupported(); |
| 243 | return; |
| 244 | } |
| 245 | |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 246 | mSelectionMgr.clearSelection(); |
| 247 | |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 248 | mClipper.clipDocumentsForCut(mModel::getItemUri, selection, mState.stack.peek()); |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 249 | |
| 250 | mDialogs.showDocumentsClipped(selection.size()); |
| 251 | } |
| 252 | |
| 253 | @Override |
| 254 | public void copyToClipboard() { |
| 255 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_COPY_CLIPBOARD); |
| 256 | Selection selection = getSelectedOrFocused(); |
| 257 | |
| 258 | if (selection.isEmpty()) { |
| 259 | return; |
| 260 | } |
| 261 | mSelectionMgr.clearSelection(); |
| 262 | |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 263 | mClipper.clipDocumentsForCopy(mModel::getItemUri, selection); |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 264 | |
| 265 | mDialogs.showDocumentsClipped(selection.size()); |
| 266 | } |
| 267 | |
Jon Mann | 253a992 | 2017-03-21 18:53:27 -0700 | [diff] [blame] | 268 | @Override |
| 269 | public void viewInOwner() { |
| 270 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_VIEW_IN_APPLICATION); |
| 271 | Selection selection = getSelectedOrFocused(); |
| 272 | |
| 273 | if (selection.isEmpty() || selection.size() > 1) { |
| 274 | return; |
| 275 | } |
| 276 | DocumentInfo doc = mModel.getDocument(selection.iterator().next()); |
| 277 | Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_SETTINGS); |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 278 | intent.setPackage(mProviders.getPackageName(doc.authority)); |
Jon Mann | 253a992 | 2017-03-21 18:53:27 -0700 | [diff] [blame] | 279 | intent.addCategory(Intent.CATEGORY_DEFAULT); |
| 280 | intent.setData(doc.derivedUri); |
| 281 | try { |
| 282 | mActivity.startActivity(intent); |
| 283 | } catch (ActivityNotFoundException e) { |
| 284 | Log.e(TAG, "Failed to view settings in application for " + doc.derivedUri, e); |
| 285 | mDialogs.showNoApplicationFound(); |
| 286 | } |
| 287 | } |
| 288 | |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 289 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 290 | @Override |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 291 | public void deleteSelectedDocuments() { |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 292 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_DELETE); |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 293 | Selection selection = getSelectedOrFocused(); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 294 | |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 295 | if (selection.isEmpty()) { |
| 296 | return; |
| 297 | } |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 298 | |
Garfield Tan | 03a3a39 | 2016-12-12 14:06:45 -0800 | [diff] [blame] | 299 | final @Nullable DocumentInfo srcParent = mState.stack.peek(); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 300 | |
| 301 | // Model must be accessed in UI thread, since underlying cursor is not threadsafe. |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 302 | List<DocumentInfo> docs = mModel.getDocuments(selection); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 303 | |
| 304 | ConfirmationCallback result = (@Result int code) -> { |
| 305 | // share the news with our caller, be it good or bad. |
Steve McKay | bd9f05a | 2016-10-10 10:18:36 -0700 | [diff] [blame] | 306 | mActionModeAddons.finishOnConfirmed(code); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 307 | |
| 308 | if (code != ConfirmationCallback.CONFIRM) { |
| 309 | return; |
| 310 | } |
| 311 | |
| 312 | UrisSupplier srcs; |
| 313 | try { |
| 314 | srcs = UrisSupplier.create( |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 315 | selection, |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 316 | mModel::getItemUri, |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 317 | mClipStore); |
Jon Mann | f657025 | 2017-03-16 14:13:55 -0700 | [diff] [blame] | 318 | } catch (Exception e) { |
| 319 | Log.e(TAG,"Failed to delete a file because we were unable to get item URIs.", e); |
| 320 | mDialogs.showFileOperationStatus( |
| 321 | FileOperations.Callback.STATUS_FAILED, |
| 322 | FileOperationService.OPERATION_DELETE, |
| 323 | selection.size()); |
| 324 | return; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 325 | } |
| 326 | |
| 327 | FileOperation operation = new FileOperation.Builder() |
| 328 | .withOpType(FileOperationService.OPERATION_DELETE) |
| 329 | .withDestination(mState.stack) |
| 330 | .withSrcs(srcs) |
Garfield Tan | 03a3a39 | 2016-12-12 14:06:45 -0800 | [diff] [blame] | 331 | .withSrcParent(srcParent == null ? null : srcParent.derivedUri) |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 332 | .build(); |
| 333 | |
Tomasz Mikolajewski | 0d83d32 | 2017-02-07 12:29:39 +0900 | [diff] [blame] | 334 | FileOperations.start(mActivity, operation, mDialogs::showFileOperationStatus, |
| 335 | FileOperations.createJobId()); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 336 | }; |
| 337 | |
| 338 | mDialogs.confirmDelete(docs, result); |
| 339 | } |
| 340 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 341 | @Override |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 342 | public void shareSelectedDocuments() { |
| 343 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_SHARE); |
| 344 | |
| 345 | Selection selection = getStableSelection(); |
| 346 | |
| 347 | assert(!selection.isEmpty()); |
| 348 | |
| 349 | // Model must be accessed in UI thread, since underlying cursor is not threadsafe. |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 350 | List<DocumentInfo> docs = mModel.loadDocuments( |
| 351 | selection, DocumentFilters.sharable(mFeatures)); |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 352 | |
| 353 | Intent intent; |
| 354 | |
| 355 | if (docs.size() == 1) { |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 356 | intent = new Intent(Intent.ACTION_SEND); |
Tomasz Mikolajewski | 5b127ac | 2016-10-24 18:24:58 +0900 | [diff] [blame] | 357 | DocumentInfo doc = docs.get(0); |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 358 | intent.setType(doc.mimeType); |
| 359 | intent.putExtra(Intent.EXTRA_STREAM, doc.derivedUri); |
| 360 | |
| 361 | } else if (docs.size() > 1) { |
| 362 | intent = new Intent(Intent.ACTION_SEND_MULTIPLE); |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 363 | |
| 364 | final ArrayList<String> mimeTypes = new ArrayList<>(); |
| 365 | final ArrayList<Uri> uris = new ArrayList<>(); |
| 366 | for (DocumentInfo doc : docs) { |
| 367 | mimeTypes.add(doc.mimeType); |
| 368 | uris.add(doc.derivedUri); |
| 369 | } |
| 370 | |
| 371 | intent.setType(MimeTypes.findCommonMimeType(mimeTypes)); |
| 372 | intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); |
| 373 | |
| 374 | } else { |
Tomasz Mikolajewski | 5b127ac | 2016-10-24 18:24:58 +0900 | [diff] [blame] | 375 | // Everything filtered out, nothing to share. |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 376 | return; |
| 377 | } |
| 378 | |
Tomasz Mikolajewski | 5b127ac | 2016-10-24 18:24:58 +0900 | [diff] [blame] | 379 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
| 380 | intent.addCategory(Intent.CATEGORY_DEFAULT); |
Tomasz Mikolajewski | 1abcfd1 | 2016-12-12 10:08:49 +0900 | [diff] [blame] | 381 | |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 382 | if (mFeatures.isVirtualFilesSharingEnabled() |
| 383 | && mModel.hasDocuments(selection, DocumentFilters.VIRTUAL)) { |
Tomasz Mikolajewski | 5b127ac | 2016-10-24 18:24:58 +0900 | [diff] [blame] | 384 | intent.addCategory(Intent.CATEGORY_TYPED_OPENABLE); |
| 385 | } |
| 386 | |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 387 | Intent chooserIntent = Intent.createChooser( |
| 388 | intent, mActivity.getResources().getText(R.string.share_via)); |
| 389 | |
| 390 | mActivity.startActivity(chooserIntent); |
| 391 | } |
| 392 | |
| 393 | @Override |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 394 | public void initLocation(Intent intent) { |
| 395 | assert(intent != null); |
| 396 | |
Garfield Tan | 40c8505 | 2017-02-02 12:44:35 -0800 | [diff] [blame] | 397 | // stack is initialized if it's restored from bundle, which means we're restoring a |
| 398 | // previously stored state. |
| 399 | if (mState.stack.isInitialized()) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 400 | if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData()); |
Garfield Tan | 5f2a9ba | 2017-05-26 14:35:44 -0700 | [diff] [blame] | 401 | restoreRootAndDirectory(); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 402 | return; |
| 403 | } |
| 404 | |
Garfield Tan | 40c8505 | 2017-02-02 12:44:35 -0800 | [diff] [blame] | 405 | if (launchToStackLocation(intent)) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 406 | if (DEBUG) Log.d(TAG, "Launched to location from stack."); |
| 407 | return; |
| 408 | } |
| 409 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 410 | if (launchToRoot(intent)) { |
| 411 | if (DEBUG) Log.d(TAG, "Launched to root for browsing."); |
| 412 | return; |
| 413 | } |
| 414 | |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 415 | if (launchToDocument(intent)) { |
| 416 | if (DEBUG) Log.d(TAG, "Launched to a document."); |
| 417 | return; |
| 418 | } |
| 419 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 420 | if (DEBUG) Log.d(TAG, "Launching directly into Home directory."); |
| 421 | loadHomeDir(); |
| 422 | } |
| 423 | |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 424 | @Override |
| 425 | protected void launchToDefaultLocation() { |
| 426 | loadHomeDir(); |
| 427 | } |
| 428 | |
Garfield Tan | 40c8505 | 2017-02-02 12:44:35 -0800 | [diff] [blame] | 429 | // If EXTRA_STACK is not null in intent, we'll skip other means of loading |
| 430 | // or restoring the stack (like URI). |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 431 | // |
| 432 | // When restoring from a stack, if a URI is present, it should only ever be: |
| 433 | // -- a launch URI: Launch URIs support sensible activity management, |
| 434 | // but don't specify a real content target) |
| 435 | // -- a fake Uri from notifications. These URIs have no authority (TODO: details). |
| 436 | // |
| 437 | // Any other URI is *sorta* unexpected...except when browsing an archive |
| 438 | // in downloads. |
Garfield Tan | 40c8505 | 2017-02-02 12:44:35 -0800 | [diff] [blame] | 439 | private boolean launchToStackLocation(Intent intent) { |
| 440 | DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK); |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 441 | if (stack == null || stack.getRoot() == null) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 442 | return false; |
| 443 | } |
| 444 | |
Garfield Tan | 40c8505 | 2017-02-02 12:44:35 -0800 | [diff] [blame] | 445 | mState.stack.reset(stack); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 446 | if (mState.stack.isEmpty()) { |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 447 | mActivity.onRootPicked(mState.stack.getRoot()); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 448 | } else { |
| 449 | mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); |
| 450 | } |
| 451 | |
| 452 | return true; |
| 453 | } |
| 454 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 455 | private boolean launchToRoot(Intent intent) { |
Garfield Tan | c5efea0 | 2017-02-22 12:58:29 -0800 | [diff] [blame] | 456 | String action = intent.getAction(); |
Garfield Tan | 8339202 | 2017-05-31 18:05:40 -0700 | [diff] [blame] | 457 | if (Intent.ACTION_VIEW.equals(action)) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 458 | Uri uri = intent.getData(); |
| 459 | if (DocumentsContract.isRootUri(mActivity, uri)) { |
| 460 | if (DEBUG) Log.d(TAG, "Launching with root URI."); |
| 461 | // If we've got a specific root to display, restore that root using a dedicated |
| 462 | // authority. That way a misbehaving provider won't result in an ANR. |
| 463 | loadRoot(uri); |
| 464 | return true; |
| 465 | } |
| 466 | } |
| 467 | return false; |
| 468 | } |
| 469 | |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 470 | private boolean launchToDocument(Intent intent) { |
Garfield Tan | c5efea0 | 2017-02-22 12:58:29 -0800 | [diff] [blame] | 471 | if (Intent.ACTION_VIEW.equals(intent.getAction())) { |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 472 | Uri uri = intent.getData(); |
| 473 | if (DocumentsContract.isDocumentUri(mActivity, uri)) { |
| 474 | return launchToDocument(intent.getData()); |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | return false; |
| 479 | } |
| 480 | |
Garfield Tan | 208945c | 2016-10-04 14:36:38 -0700 | [diff] [blame] | 481 | @Override |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 482 | public void showChooserForDoc(DocumentInfo doc) { |
Tomasz Mikolajewski | ac3e63e | 2017-02-13 10:08:58 +0900 | [diff] [blame] | 483 | assert(!doc.isDirectory()); |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 484 | |
| 485 | if (manageDocument(doc)) { |
| 486 | Log.w(TAG, "Open with is not yet supported for managed doc."); |
| 487 | return; |
| 488 | } |
| 489 | |
| 490 | Intent intent = Intent.createChooser(buildViewIntent(doc), null); |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 491 | if (Features.OMC_RUNTIME) { |
Ben Lin | df0fe89 | 2016-10-18 17:36:05 -0700 | [diff] [blame] | 492 | intent.putExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, false); |
| 493 | } |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 494 | try { |
| 495 | mActivity.startActivity(intent); |
| 496 | } catch (ActivityNotFoundException e) { |
| 497 | mDialogs.showNoApplicationFound(); |
| 498 | } |
| 499 | } |
| 500 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 501 | private void onDocumentPicked(DocumentInfo doc, @ViewType int type, @ViewType int fallback) { |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 502 | if (doc.isContainer()) { |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 503 | openContainerDocument(doc); |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 504 | return; |
| 505 | } |
| 506 | |
| 507 | if (manageDocument(doc)) { |
| 508 | return; |
| 509 | } |
| 510 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 511 | switch (type) { |
| 512 | case VIEW_TYPE_REGULAR: |
| 513 | if (viewDocument(doc)) { |
| 514 | return; |
| 515 | } |
| 516 | break; |
| 517 | |
| 518 | case VIEW_TYPE_PREVIEW: |
| 519 | if (previewDocument(doc)) { |
| 520 | return; |
| 521 | } |
| 522 | break; |
| 523 | |
| 524 | default: |
| 525 | throw new IllegalArgumentException("Illegal view type."); |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 526 | } |
| 527 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 528 | switch (fallback) { |
| 529 | case VIEW_TYPE_REGULAR: |
| 530 | if (viewDocument(doc)) { |
| 531 | return; |
| 532 | } |
| 533 | break; |
| 534 | |
| 535 | case VIEW_TYPE_PREVIEW: |
| 536 | if (previewDocument(doc)) { |
| 537 | return; |
| 538 | } |
| 539 | break; |
| 540 | |
| 541 | case VIEW_TYPE_NONE: |
| 542 | break; |
| 543 | |
| 544 | default: |
| 545 | throw new IllegalArgumentException("Illegal fallback view type."); |
| 546 | } |
| 547 | |
| 548 | // Failed to view including fallback, and it's in an archive. |
| 549 | if (type != VIEW_TYPE_NONE && fallback != VIEW_TYPE_NONE && doc.isInArchive()) { |
| 550 | mDialogs.showViewInArchivesUnsupported(); |
| 551 | } |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 552 | } |
| 553 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 554 | private boolean viewDocument(DocumentInfo doc) { |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 555 | if (doc.isPartial()) { |
| 556 | Log.w(TAG, "Can't view partial file."); |
| 557 | return false; |
| 558 | } |
| 559 | |
Tomasz Mikolajewski | e535c57 | 2016-11-25 13:58:35 +0900 | [diff] [blame] | 560 | if (doc.isInArchive()) { |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 561 | Log.w(TAG, "Can't view files in archives."); |
Tomasz Mikolajewski | e535c57 | 2016-11-25 13:58:35 +0900 | [diff] [blame] | 562 | return false; |
| 563 | } |
| 564 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 565 | if (doc.isDirectory()) { |
| 566 | Log.w(TAG, "Can't view directories."); |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 567 | return true; |
| 568 | } |
| 569 | |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 570 | Intent intent = buildViewIntent(doc); |
| 571 | if (DEBUG && intent.getClipData() != null) { |
| 572 | Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData()); |
| 573 | } |
| 574 | |
| 575 | try { |
| 576 | mActivity.startActivity(intent); |
| 577 | return true; |
| 578 | } catch (ActivityNotFoundException e) { |
| 579 | mDialogs.showNoApplicationFound(); |
| 580 | } |
| 581 | return false; |
| 582 | } |
| 583 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 584 | private boolean previewDocument(DocumentInfo doc) { |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 585 | if (doc.isPartial()) { |
| 586 | Log.w(TAG, "Can't view partial file."); |
| 587 | return false; |
| 588 | } |
| 589 | |
| 590 | Intent intent = new QuickViewIntentBuilder( |
| 591 | mActivity.getPackageManager(), |
| 592 | mActivity.getResources(), |
| 593 | doc, |
Ben Lin | f23cbf4 | 2016-12-22 15:12:13 -0800 | [diff] [blame] | 594 | mModel).build(); |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 595 | |
| 596 | if (intent != null) { |
| 597 | // TODO: un-work around issue b/24963914. Should be fixed soon. |
| 598 | try { |
| 599 | mActivity.startActivity(intent); |
| 600 | return true; |
| 601 | } catch (SecurityException e) { |
| 602 | // Carry on to regular view mode. |
| 603 | Log.e(TAG, "Caught security error: " + e.getLocalizedMessage()); |
| 604 | } |
| 605 | } |
| 606 | |
| 607 | return false; |
| 608 | } |
| 609 | |
| 610 | private boolean manageDocument(DocumentInfo doc) { |
| 611 | if (isManagedDownload(doc)) { |
| 612 | // First try managing the document; we expect manager to filter |
| 613 | // based on authority, so we don't grant. |
| 614 | Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT); |
| 615 | manage.setData(doc.derivedUri); |
| 616 | try { |
| 617 | mActivity.startActivity(manage); |
| 618 | return true; |
| 619 | } catch (ActivityNotFoundException ex) { |
| 620 | // Fall back to regular handling. |
| 621 | } |
| 622 | } |
| 623 | |
| 624 | return false; |
| 625 | } |
| 626 | |
| 627 | private boolean isManagedDownload(DocumentInfo doc) { |
| 628 | // Anything on downloads goes through the back through downloads manager |
| 629 | // (that's the MANAGE_DOCUMENT bit). |
| 630 | // This is done for two reasons: |
| 631 | // 1) The file in question might be a failed/queued or otherwise have some |
| 632 | // specialized download handling. |
| 633 | // 2) For APKs, the download manager will add on some important security stuff |
| 634 | // like origin URL. |
| 635 | // 3) For partial files, the download manager will offer to restart/retry downloads. |
| 636 | |
| 637 | // All other files not on downloads, event APKs, would get no benefit from this |
| 638 | // treatment, thusly the "isDownloads" check. |
| 639 | |
| 640 | // Launch MANAGE_DOCUMENTS only for the root level files, so it's not called for |
| 641 | // files in archives. Also, if the activity is already browsing a ZIP from downloads, |
| 642 | // then skip MANAGE_DOCUMENTS. |
| 643 | if (Intent.ACTION_VIEW.equals(mActivity.getIntent().getAction()) |
| 644 | && mState.stack.size() > 1) { |
| 645 | // viewing the contents of an archive. |
| 646 | return false; |
| 647 | } |
| 648 | |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 649 | // management is only supported in downloads. |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 650 | if (mActivity.getCurrentRoot().isDownloads()) { |
| 651 | // and only and only on APKs or partial files. |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 652 | return MimeTypes.isApkType(doc.mimeType) |
Steve McKay | eed2f4e | 2016-10-03 20:30:52 -0700 | [diff] [blame] | 653 | || doc.isPartial(); |
| 654 | } |
| 655 | |
| 656 | return false; |
| 657 | } |
| 658 | |
| 659 | private Intent buildViewIntent(DocumentInfo doc) { |
| 660 | Intent intent = new Intent(Intent.ACTION_VIEW); |
| 661 | intent.setDataAndType(doc.derivedUri, doc.mimeType); |
| 662 | |
| 663 | // Downloads has traditionally added the WRITE permission |
| 664 | // in the TrampolineActivity. Since this behavior is long |
| 665 | // established, we set the same permission for non-managed files |
| 666 | // This ensures consistent behavior between the Downloads root |
| 667 | // and other roots. |
| 668 | int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION; |
| 669 | if (doc.isWriteSupported()) { |
| 670 | flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION; |
| 671 | } |
| 672 | intent.setFlags(flags); |
| 673 | |
| 674 | return intent; |
| 675 | } |
| 676 | |
Dooper | 0930d4c | 2017-06-02 10:32:00 -0700 | [diff] [blame] | 677 | @Override |
Austin Kolander | f5042d0 | 2017-06-08 09:20:30 -0700 | [diff] [blame] | 678 | public void showInspector(DocumentInfo doc) { |
Steve McKay | fbf6858 | 2017-06-09 15:28:28 -0700 | [diff] [blame] | 679 | Metrics.logUserAction(mActivity, Metrics.USER_ACTION_INSPECTOR); |
Steve McKay | f433d20 | 2017-07-12 18:46:09 -0700 | [diff] [blame^] | 680 | Intent intent = new Intent(mActivity, InspectorActivity.class); |
Steve McKay | 84440eb | 2017-06-23 12:35:15 -0700 | [diff] [blame] | 681 | intent.putExtra( |
| 682 | Shared.EXTRA_SHOW_DEBUG, |
| 683 | mFeatures.isDebugSupportEnabled() |
Steve McKay | f433d20 | 2017-07-12 18:46:09 -0700 | [diff] [blame^] | 684 | || DebugFlags.getDocumentDetailsEnabled()); |
Steve McKay | fbf6858 | 2017-06-09 15:28:28 -0700 | [diff] [blame] | 685 | intent.setData(doc.derivedUri); |
Dooper | 0930d4c | 2017-06-02 10:32:00 -0700 | [diff] [blame] | 686 | mActivity.startActivity(intent); |
| 687 | } |
| 688 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 689 | public interface Addons extends CommonAddons { |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 690 | } |
Garfield Tan | 7d75f7b | 2016-09-20 16:33:24 -0700 | [diff] [blame] | 691 | } |