Steve McKay | 739f94b | 2016-09-22 14:54:23 -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 | |
| 17 | package com.android.documentsui; |
| 18 | |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 19 | import static com.android.documentsui.base.DocumentInfo.getCursorInt; |
| 20 | import static com.android.documentsui.base.DocumentInfo.getCursorString; |
Felipe Leme | 9de5807 | 2018-01-19 16:40:04 -0800 | [diff] [blame] | 21 | import static com.android.documentsui.base.SharedMinimal.DEBUG; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 22 | |
Ben Lin | 8003008 | 2017-05-01 18:50:05 -0700 | [diff] [blame] | 23 | import android.app.PendingIntent; |
Tony Huang | 94fc11a | 2019-10-30 18:00:20 +0800 | [diff] [blame] | 24 | import android.content.ComponentName; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 25 | import android.content.Context; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 26 | import android.content.Intent; |
Ben Lin | 8003008 | 2017-05-01 18:50:05 -0700 | [diff] [blame] | 27 | import android.content.IntentSender; |
Tony Huang | 94fc11a | 2019-10-30 18:00:20 +0800 | [diff] [blame] | 28 | import android.content.pm.PackageManager; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 29 | import android.content.pm.ResolveInfo; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 30 | import android.database.Cursor; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 31 | import android.net.Uri; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 32 | import android.os.Bundle; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 33 | import android.os.Parcelable; |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 34 | import android.provider.DocumentsContract; |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 35 | import android.util.Log; |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 36 | import android.util.Pair; |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 37 | import android.view.DragEvent; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 38 | |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 39 | import androidx.annotation.VisibleForTesting; |
Tony Huang | 8d8d92f | 2018-09-13 14:41:16 +0800 | [diff] [blame] | 40 | import androidx.fragment.app.FragmentActivity; |
Tony Huang | 35ad6bd | 2018-11-06 14:39:40 +0800 | [diff] [blame] | 41 | import androidx.loader.app.LoaderManager.LoaderCallbacks; |
| 42 | import androidx.loader.content.Loader; |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 43 | import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails; |
| 44 | import androidx.recyclerview.selection.MutableSelection; |
| 45 | import androidx.recyclerview.selection.SelectionTracker; |
| 46 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 47 | import com.android.documentsui.AbstractActionHandler.CommonAddons; |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 48 | import com.android.documentsui.LoadDocStackTask.LoadDocStackCallback; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 49 | import com.android.documentsui.base.BooleanConsumer; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 50 | import com.android.documentsui.base.DocumentInfo; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 51 | import com.android.documentsui.base.DocumentStack; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 52 | import com.android.documentsui.base.Lookup; |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 53 | import com.android.documentsui.base.Providers; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 54 | import com.android.documentsui.base.RootInfo; |
| 55 | import com.android.documentsui.base.Shared; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 56 | import com.android.documentsui.base.State; |
Tomasz Mikolajewski | dbd6b8b | 2016-09-29 15:27:37 +0900 | [diff] [blame] | 57 | import com.android.documentsui.dirlist.AnimationView; |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 58 | import com.android.documentsui.dirlist.AnimationView.AnimationType; |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 59 | import com.android.documentsui.dirlist.FocusHandler; |
Steve McKay | b6006b2 | 2016-09-29 09:23:45 -0700 | [diff] [blame] | 60 | import com.android.documentsui.files.LauncherActivity; |
Steve McKay | 3a26823 | 2016-10-19 11:15:47 -0700 | [diff] [blame] | 61 | import com.android.documentsui.queries.SearchViewManager; |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 62 | import com.android.documentsui.roots.GetRootDocumentTask; |
Ivan Chiang | 9b9a282 | 2018-09-19 17:03:22 +0800 | [diff] [blame] | 63 | import com.android.documentsui.roots.LoadFirstRootTask; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 64 | import com.android.documentsui.roots.LoadRootTask; |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 65 | import com.android.documentsui.roots.ProvidersAccess; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 66 | import com.android.documentsui.sidebar.EjectRootTask; |
Tony Huang | 243cf9c | 2018-10-16 16:15:18 +0800 | [diff] [blame] | 67 | import com.android.documentsui.sorting.SortListFragment; |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 68 | import com.android.documentsui.ui.Snackbars; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 69 | |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 70 | import java.util.ArrayList; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 71 | import java.util.List; |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 72 | import java.util.Objects; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 73 | import java.util.concurrent.Executor; |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 74 | import java.util.function.Consumer; |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 75 | |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 76 | import javax.annotation.Nullable; |
| 77 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 78 | /** |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 79 | * Provides support for specializing the actions (openDocument etc.) to the host activity. |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 80 | */ |
Tony Huang | 8d8d92f | 2018-09-13 14:41:16 +0800 | [diff] [blame] | 81 | public abstract class AbstractActionHandler<T extends FragmentActivity & CommonAddons> |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 82 | implements ActionHandler { |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 83 | |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 84 | @VisibleForTesting |
Ben Lin | 8003008 | 2017-05-01 18:50:05 -0700 | [diff] [blame] | 85 | public static final int CODE_FORWARD = 42; |
| 86 | public static final int CODE_AUTHENTICATION = 43; |
| 87 | |
| 88 | @VisibleForTesting |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 89 | static final int LOADER_ID = 42; |
| 90 | |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 91 | private static final String TAG = "AbstractActionHandler"; |
Ben Lin | e9abd2d | 2016-12-06 11:39:52 -0800 | [diff] [blame] | 92 | private static final int REFRESH_SPINNER_TIMEOUT = 500; |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 93 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 94 | protected final T mActivity; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 95 | protected final State mState; |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 96 | protected final ProvidersAccess mProviders; |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 97 | protected final DocumentsAccess mDocs; |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 98 | protected final FocusHandler mFocusHandler; |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 99 | protected final SelectionTracker<String> mSelectionMgr; |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 100 | protected final SearchViewManager mSearchMgr; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 101 | protected final Lookup<String, Executor> mExecutors; |
Steve McKay | 2979b21 | 2017-04-11 14:40:01 -0700 | [diff] [blame] | 102 | protected final Injector<?> mInjector; |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 103 | |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 104 | private final LoaderBindings mBindings; |
| 105 | |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 106 | private Runnable mDisplayStateChangedListener; |
| 107 | |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 108 | private ContentLock mContentLock; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 109 | |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 110 | @Override |
| 111 | public void registerDisplayStateChangedListener(Runnable l) { |
| 112 | mDisplayStateChangedListener = l; |
| 113 | } |
| 114 | @Override |
| 115 | public void unregisterDisplayStateChangedListener(Runnable l) { |
| 116 | if (mDisplayStateChangedListener == l) { |
| 117 | mDisplayStateChangedListener = null; |
| 118 | } |
| 119 | } |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 120 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 121 | public AbstractActionHandler( |
| 122 | T activity, |
| 123 | State state, |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 124 | ProvidersAccess providers, |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 125 | DocumentsAccess docs, |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 126 | SearchViewManager searchMgr, |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 127 | Lookup<String, Executor> executors, |
Steve McKay | 2979b21 | 2017-04-11 14:40:01 -0700 | [diff] [blame] | 128 | Injector<?> injector) { |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 129 | |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 130 | assert(activity != null); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 131 | assert(state != null); |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 132 | assert(providers != null); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 133 | assert(searchMgr != null); |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 134 | assert(docs != null); |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 135 | assert(injector != null); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 136 | |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 137 | mActivity = activity; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 138 | mState = state; |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 139 | mProviders = providers; |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 140 | mDocs = docs; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 141 | mFocusHandler = injector.focusManager; |
| 142 | mSelectionMgr = injector.selectionMgr; |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 143 | mSearchMgr = searchMgr; |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 144 | mExecutors = executors; |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 145 | mInjector = injector; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 146 | |
| 147 | mBindings = new LoaderBindings(); |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 151 | public void ejectRoot(RootInfo root, BooleanConsumer listener) { |
| 152 | new EjectRootTask( |
| 153 | mActivity.getContentResolver(), |
| 154 | root.authority, |
| 155 | root.rootId, |
| 156 | listener).executeOnExecutor(ProviderExecutor.forAuthority(root.authority)); |
| 157 | } |
| 158 | |
| 159 | @Override |
Ben Lin | 8003008 | 2017-05-01 18:50:05 -0700 | [diff] [blame] | 160 | public void startAuthentication(PendingIntent intent) { |
| 161 | try { |
| 162 | mActivity.startIntentSenderForResult(intent.getIntentSender(), CODE_AUTHENTICATION, |
| 163 | null, 0, 0, 0); |
| 164 | } catch (IntentSender.SendIntentException cancelled) { |
| 165 | Log.d(TAG, "Authentication Pending Intent either canceled or ignored."); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | @Override |
| 170 | public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| 171 | switch (requestCode) { |
| 172 | case CODE_AUTHENTICATION: |
| 173 | onAuthenticationResult(resultCode); |
| 174 | break; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | private void onAuthenticationResult(int resultCode) { |
Tony Huang | 8d8d92f | 2018-09-13 14:41:16 +0800 | [diff] [blame] | 179 | if (resultCode == FragmentActivity.RESULT_OK) { |
Ben Lin | 8003008 | 2017-05-01 18:50:05 -0700 | [diff] [blame] | 180 | Log.v(TAG, "Authentication was successful. Refreshing directory now."); |
| 181 | mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | @Override |
Ben Lin | 30b0dc1 | 2017-03-07 15:37:16 -0800 | [diff] [blame] | 186 | public void getRootDocument(RootInfo root, int timeout, Consumer<DocumentInfo> callback) { |
| 187 | GetRootDocumentTask task = new GetRootDocumentTask( |
| 188 | root, |
| 189 | mActivity, |
| 190 | timeout, |
| 191 | mDocs, |
| 192 | callback); |
| 193 | |
| 194 | task.executeOnExecutor(mExecutors.lookup(root.authority)); |
| 195 | } |
| 196 | |
| 197 | @Override |
Ben Lin | e9abd2d | 2016-12-06 11:39:52 -0800 | [diff] [blame] | 198 | public void refreshDocument(DocumentInfo doc, BooleanConsumer callback) { |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 199 | RefreshTask task = new RefreshTask( |
| 200 | mInjector.features, |
| 201 | mState, |
| 202 | doc, |
| 203 | REFRESH_SPINNER_TIMEOUT, |
| 204 | mActivity.getApplicationContext(), |
| 205 | mActivity::isDestroyed, |
Ben Lin | e9abd2d | 2016-12-06 11:39:52 -0800 | [diff] [blame] | 206 | callback); |
| 207 | task.executeOnExecutor(mExecutors.lookup(doc == null ? null : doc.authority)); |
| 208 | } |
| 209 | |
| 210 | @Override |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 211 | public void openSelectedInNewWindow() { |
| 212 | throw new UnsupportedOperationException("Can't open in new window."); |
| 213 | } |
| 214 | |
| 215 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 216 | public void openInNewWindow(DocumentStack path) { |
shawnlin | 9cee68f | 2019-01-25 11:20:18 +0800 | [diff] [blame] | 217 | Metrics.logUserAction(MetricConsts.USER_ACTION_NEW_WINDOW); |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 218 | |
| 219 | Intent intent = LauncherActivity.createLaunchIntent(mActivity); |
| 220 | intent.putExtra(Shared.EXTRA_STACK, (Parcelable) path); |
| 221 | |
| 222 | // Multi-window necessitates we pick how we are launched. |
| 223 | // By default we'd be launched in-place above the existing app. |
| 224 | // By setting launch-to-side ActivityManager will open us to side. |
| 225 | if (mActivity.isInMultiWindowMode()) { |
| 226 | intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT); |
| 227 | } |
| 228 | |
| 229 | mActivity.startActivity(intent); |
| 230 | } |
| 231 | |
| 232 | @Override |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 233 | public boolean openItem(ItemDetails<String> doc, @ViewType int type, @ViewType int fallback) { |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 234 | throw new UnsupportedOperationException("Can't open document."); |
| 235 | } |
| 236 | |
Steve McKay | 3a24e6c | 2017-08-07 13:30:04 -0700 | [diff] [blame] | 237 | @Override |
Austin Kolander | f5042d0 | 2017-06-08 09:20:30 -0700 | [diff] [blame] | 238 | public void showInspector(DocumentInfo doc) { |
Dooper | 0930d4c | 2017-06-02 10:32:00 -0700 | [diff] [blame] | 239 | throw new UnsupportedOperationException("Can't open properties."); |
| 240 | } |
| 241 | |
Tomasz Mikolajewski | d22cc18 | 2017-03-15 16:13:46 +0900 | [diff] [blame] | 242 | @Override |
Ben Lin | d8d0ad2 | 2017-01-11 13:30:50 -0800 | [diff] [blame] | 243 | public void springOpenDirectory(DocumentInfo doc) { |
| 244 | throw new UnsupportedOperationException("Can't spring open directories."); |
| 245 | } |
| 246 | |
| 247 | @Override |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 248 | public void openSettings(RootInfo root) { |
| 249 | throw new UnsupportedOperationException("Can't open settings."); |
| 250 | } |
| 251 | |
| 252 | @Override |
| 253 | public void openRoot(ResolveInfo app) { |
| 254 | throw new UnsupportedOperationException("Can't open an app."); |
| 255 | } |
| 256 | |
| 257 | @Override |
| 258 | public void showAppDetails(ResolveInfo info) { |
| 259 | throw new UnsupportedOperationException("Can't show app details."); |
| 260 | } |
| 261 | |
| 262 | @Override |
Ben Lin | 174fc2e | 2017-03-01 17:53:20 -0800 | [diff] [blame] | 263 | public boolean dropOn(DragEvent event, RootInfo root) { |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 264 | throw new UnsupportedOperationException("Can't open an app."); |
| 265 | } |
| 266 | |
| 267 | @Override |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 268 | public void pasteIntoFolder(RootInfo root) { |
| 269 | throw new UnsupportedOperationException("Can't paste into folder."); |
| 270 | } |
| 271 | |
| 272 | @Override |
Jon Mann | 253a992 | 2017-03-21 18:53:27 -0700 | [diff] [blame] | 273 | public void viewInOwner() { |
| 274 | throw new UnsupportedOperationException("Can't view in application."); |
| 275 | } |
| 276 | |
| 277 | @Override |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 278 | public void selectAllFiles() { |
shawnlin | 9cee68f | 2019-01-25 11:20:18 +0800 | [diff] [blame] | 279 | Metrics.logUserAction(MetricConsts.USER_ACTION_SELECT_ALL); |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 280 | Model model = mInjector.getModel(); |
| 281 | |
| 282 | // Exclude disabled files |
| 283 | List<String> enabled = new ArrayList<>(); |
| 284 | for (String id : model.getModelIds()) { |
| 285 | Cursor cursor = model.getItem(id); |
| 286 | if (cursor == null) { |
| 287 | Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id); |
| 288 | continue; |
| 289 | } |
| 290 | String docMimeType = getCursorString( |
| 291 | cursor, DocumentsContract.Document.COLUMN_MIME_TYPE); |
| 292 | int docFlags = getCursorInt(cursor, DocumentsContract.Document.COLUMN_FLAGS); |
| 293 | if (mInjector.config.isDocumentEnabled(docMimeType, docFlags, mState)) { |
| 294 | enabled.add(id); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | // Only select things currently visible in the adapter. |
| 299 | boolean changed = mSelectionMgr.setItemsSelected(enabled, true); |
| 300 | if (changed) { |
| 301 | mDisplayStateChangedListener.run(); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | @Override |
Tony Huang | 76c6c9d | 2019-08-12 18:04:06 +0800 | [diff] [blame] | 306 | public void deselectAllFiles() { |
| 307 | mSelectionMgr.clearSelection(); |
| 308 | } |
| 309 | |
| 310 | @Override |
Ben Lin | ff7f3ae | 2017-04-25 16:08:52 -0700 | [diff] [blame] | 311 | public void showCreateDirectoryDialog() { |
shawnlin | 9cee68f | 2019-01-25 11:20:18 +0800 | [diff] [blame] | 312 | Metrics.logUserAction(MetricConsts.USER_ACTION_CREATE_DIR); |
Ben Lin | ff7f3ae | 2017-04-25 16:08:52 -0700 | [diff] [blame] | 313 | |
Tony Huang | 8d8d92f | 2018-09-13 14:41:16 +0800 | [diff] [blame] | 314 | CreateDirectoryFragment.show(mActivity.getSupportFragmentManager()); |
Ben Lin | ff7f3ae | 2017-04-25 16:08:52 -0700 | [diff] [blame] | 315 | } |
| 316 | |
| 317 | @Override |
Tony Huang | 243cf9c | 2018-10-16 16:15:18 +0800 | [diff] [blame] | 318 | public void showSortDialog() { |
Tony Huang | 8d8d92f | 2018-09-13 14:41:16 +0800 | [diff] [blame] | 319 | SortListFragment.show(mActivity.getSupportFragmentManager(), mState.sortModel); |
Tony Huang | 243cf9c | 2018-10-16 16:15:18 +0800 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | @Override |
Jon Mann | 30d8c79 | 2017-02-21 17:44:49 -0800 | [diff] [blame] | 323 | @Nullable |
| 324 | public DocumentInfo renameDocument(String name, DocumentInfo document) { |
| 325 | throw new UnsupportedOperationException("Can't rename documents."); |
| 326 | } |
| 327 | |
| 328 | @Override |
Garfield Tan | 208945c | 2016-10-04 14:36:38 -0700 | [diff] [blame] | 329 | public void showChooserForDoc(DocumentInfo doc) { |
| 330 | throw new UnsupportedOperationException("Show chooser for doc not supported!"); |
| 331 | } |
| 332 | |
| 333 | @Override |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 334 | public void openRootDocument(@Nullable DocumentInfo rootDoc) { |
| 335 | if (rootDoc == null) { |
| 336 | // There are 2 cases where rootDoc is null -- 1) loading recents; 2) failed to load root |
| 337 | // document. Either case we should call refreshCurrentRootAndDirectory() to let |
| 338 | // DirectoryFragment update UI. |
| 339 | mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); |
| 340 | } else { |
| 341 | openContainerDocument(rootDoc); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | @Override |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 346 | public void openContainerDocument(DocumentInfo doc) { |
| 347 | assert(doc.isContainer()); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 348 | |
| 349 | if (mSearchMgr.isSearching()) { |
| 350 | loadDocument( |
| 351 | doc.derivedUri, |
| 352 | (@Nullable DocumentStack stack) -> openFolderInSearchResult(stack, doc)); |
| 353 | } else { |
| 354 | openChildContainer(doc); |
| 355 | } |
| 356 | } |
| 357 | |
Tony Huang | 7a7e7df | 2018-11-06 17:16:47 +0800 | [diff] [blame] | 358 | @Override |
| 359 | public boolean previewItem(ItemDetails<String> doc) { |
| 360 | throw new UnsupportedOperationException("Can't handle preview."); |
| 361 | } |
| 362 | |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 363 | private void openFolderInSearchResult(@Nullable DocumentStack stack, DocumentInfo doc) { |
| 364 | if (stack == null) { |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 365 | mState.stack.popToRootDocument(); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 366 | |
| 367 | // Update navigator to give horizontal breadcrumb a chance to update documents. It |
| 368 | // doesn't update its content if the size of document stack doesn't change. |
| 369 | // TODO: update breadcrumb to take range update. |
| 370 | mActivity.updateNavigator(); |
| 371 | |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 372 | mState.stack.push(doc); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 373 | } else { |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 374 | if (!Objects.equals(mState.stack.getRoot(), stack.getRoot())) { |
| 375 | Log.w(TAG, "Provider returns " + stack.getRoot() + " rather than expected " |
| 376 | + mState.stack.getRoot()); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 377 | } |
| 378 | |
Garfield Tan | 79a572b | 2017-07-20 16:40:51 -0700 | [diff] [blame] | 379 | final DocumentInfo top = stack.peek(); |
| 380 | if (top.isArchive()) { |
| 381 | // Swap the zip file in original provider and the one provided by ArchiveProvider. |
| 382 | stack.pop(); |
| 383 | stack.push(mDocs.getArchiveDocument(top.derivedUri)); |
| 384 | } |
| 385 | |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 386 | mState.stack.reset(); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 387 | // Update navigator to give horizontal breadcrumb a chance to update documents. It |
| 388 | // doesn't update its content if the size of document stack doesn't change. |
| 389 | // TODO: update breadcrumb to take range update. |
| 390 | mActivity.updateNavigator(); |
| 391 | |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 392 | mState.stack.reset(stack); |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 393 | } |
| 394 | |
| 395 | // Show an opening animation only if pressing "back" would get us back to the |
| 396 | // previous directory. Especially after opening a root document, pressing |
| 397 | // back, wouldn't go to the previous root, but close the activity. |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 398 | final int anim = (mState.stack.hasLocationChanged() && mState.stack.size() > 1) |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 399 | ? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE; |
| 400 | mActivity.refreshCurrentRootAndDirectory(anim); |
| 401 | } |
| 402 | |
| 403 | private void openChildContainer(DocumentInfo doc) { |
Tomasz Mikolajewski | dbd6b8b | 2016-09-29 15:27:37 +0900 | [diff] [blame] | 404 | DocumentInfo currentDoc = null; |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 405 | |
Tomasz Mikolajewski | dbd6b8b | 2016-09-29 15:27:37 +0900 | [diff] [blame] | 406 | if (doc.isDirectory()) { |
| 407 | // Regular directory. |
| 408 | currentDoc = doc; |
| 409 | } else if (doc.isArchive()) { |
| 410 | // Archive. |
| 411 | currentDoc = mDocs.getArchiveDocument(doc.derivedUri); |
| 412 | } |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 413 | |
Tomasz Mikolajewski | dbd6b8b | 2016-09-29 15:27:37 +0900 | [diff] [blame] | 414 | assert(currentDoc != null); |
| 415 | mActivity.notifyDirectoryNavigated(currentDoc.derivedUri); |
Tomasz Mikolajewski | dbd6b8b | 2016-09-29 15:27:37 +0900 | [diff] [blame] | 416 | |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 417 | mState.stack.push(currentDoc); |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 418 | // Show an opening animation only if pressing "back" would get us back to the |
| 419 | // previous directory. Especially after opening a root document, pressing |
| 420 | // back, wouldn't go to the previous root, but close the activity. |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 421 | final int anim = (mState.stack.hasLocationChanged() && mState.stack.size() > 1) |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 422 | ? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE; |
| 423 | mActivity.refreshCurrentRootAndDirectory(anim); |
| 424 | } |
| 425 | |
| 426 | @Override |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 427 | public void setDebugMode(boolean enabled) { |
Steve McKay | 2979b21 | 2017-04-11 14:40:01 -0700 | [diff] [blame] | 428 | if (!mInjector.features.isDebugSupportEnabled()) { |
| 429 | return; |
| 430 | } |
| 431 | |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 432 | mState.debugMode = enabled; |
Steve McKay | 2979b21 | 2017-04-11 14:40:01 -0700 | [diff] [blame] | 433 | mInjector.features.forceFeature(R.bool.feature_command_interceptor, enabled); |
Steve McKay | 84440eb | 2017-06-23 12:35:15 -0700 | [diff] [blame] | 434 | mInjector.features.forceFeature(R.bool.feature_inspector, enabled); |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 435 | mActivity.invalidateOptionsMenu(); |
| 436 | |
| 437 | if (enabled) { |
| 438 | showDebugMessage(); |
| 439 | } else { |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 440 | mActivity.getWindow().setStatusBarColor( |
Tony Huang | 4ce11e2 | 2019-09-25 19:02:46 +0800 | [diff] [blame] | 441 | mActivity.getResources().getColor(R.color.app_background_color)); |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 442 | } |
| 443 | } |
| 444 | |
| 445 | @Override |
| 446 | public void showDebugMessage() { |
Steve McKay | 2979b21 | 2017-04-11 14:40:01 -0700 | [diff] [blame] | 447 | assert (mInjector.features.isDebugSupportEnabled()); |
| 448 | |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 449 | int[] colors = mInjector.debugHelper.getNextColors(); |
| 450 | Pair<String, Integer> messagePair = mInjector.debugHelper.getNextMessage(); |
| 451 | |
| 452 | Snackbars.showCustomTextWithImage(mActivity, messagePair.first, messagePair.second); |
| 453 | |
Jon Mann | eb1d11b | 2017-04-01 15:36:59 -0700 | [diff] [blame] | 454 | mActivity.getWindow().setStatusBarColor(colors[1]); |
| 455 | } |
| 456 | |
| 457 | @Override |
Tony Huang | 94fc11a | 2019-10-30 18:00:20 +0800 | [diff] [blame] | 458 | public void switchLauncherIcon() { |
| 459 | PackageManager pm = mActivity.getPackageManager(); |
| 460 | if (pm != null) { |
| 461 | final boolean enalbled = Shared.isLauncherEnabled(mActivity); |
| 462 | ComponentName component = new ComponentName( |
| 463 | mActivity.getPackageName(), Shared.LAUNCHER_TARGET_CLASS); |
| 464 | pm.setComponentEnabledSetting(component, enalbled |
| 465 | ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED |
| 466 | : PackageManager.COMPONENT_ENABLED_STATE_ENABLED, |
| 467 | PackageManager.DONT_KILL_APP); |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | @Override |
Ben Lin | d947f01 | 2016-10-18 14:32:49 -0700 | [diff] [blame] | 472 | public void cutToClipboard() { |
| 473 | throw new UnsupportedOperationException("Cut not supported!"); |
| 474 | } |
| 475 | |
| 476 | @Override |
| 477 | public void copyToClipboard() { |
| 478 | throw new UnsupportedOperationException("Copy not supported!"); |
| 479 | } |
| 480 | |
| 481 | @Override |
shawnlin | 8dafe61 | 2019-08-14 20:10:18 +0800 | [diff] [blame] | 482 | public void showDeleteDialog() { |
| 483 | throw new UnsupportedOperationException("Delete not supported!"); |
| 484 | } |
| 485 | |
| 486 | @Override |
| 487 | public void deleteSelectedDocuments(List<DocumentInfo> docs, DocumentInfo srcParent) { |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 488 | throw new UnsupportedOperationException("Delete not supported!"); |
| 489 | } |
| 490 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 491 | @Override |
Steve McKay | d071895 | 2016-10-10 13:43:36 -0700 | [diff] [blame] | 492 | public void shareSelectedDocuments() { |
| 493 | throw new UnsupportedOperationException("Share not supported!"); |
| 494 | } |
| 495 | |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 496 | protected final void loadDocument(Uri uri, LoadDocStackCallback callback) { |
| 497 | new LoadDocStackTask( |
| 498 | mActivity, |
Jon Mann | 9bd4099 | 2017-03-24 12:34:34 -0700 | [diff] [blame] | 499 | mProviders, |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 500 | mDocs, |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 501 | callback |
Garfield Tan | 2a83742 | 2016-10-19 11:50:45 -0700 | [diff] [blame] | 502 | ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()), uri); |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 503 | } |
| 504 | |
| 505 | @Override |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 506 | public final void loadRoot(Uri uri) { |
Tony Huang | 957be8c | 2019-07-30 15:49:36 +0800 | [diff] [blame] | 507 | new LoadRootTask<>(mActivity, mProviders, uri, this::onRootLoaded) |
Steve McKay | def4868 | 2016-10-03 09:07:38 -0700 | [diff] [blame] | 508 | .executeOnExecutor(mExecutors.lookup(uri.getAuthority())); |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 509 | } |
| 510 | |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 511 | @Override |
Ivan Chiang | 9b9a282 | 2018-09-19 17:03:22 +0800 | [diff] [blame] | 512 | public final void loadFirstRoot(Uri uri) { |
Tony Huang | 957be8c | 2019-07-30 15:49:36 +0800 | [diff] [blame] | 513 | new LoadFirstRootTask<>(mActivity, mProviders, uri, this::onRootLoaded) |
Ivan Chiang | 9b9a282 | 2018-09-19 17:03:22 +0800 | [diff] [blame] | 514 | .executeOnExecutor(mExecutors.lookup(uri.getAuthority())); |
| 515 | } |
| 516 | |
| 517 | @Override |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 518 | public void loadDocumentsForCurrentStack() { |
| 519 | DocumentStack stack = mState.stack; |
| 520 | if (!stack.isRecents() && stack.isEmpty()) { |
| 521 | DirectoryResult result = new DirectoryResult(); |
| 522 | |
| 523 | // TODO (b/35996595): Consider plumbing through the actual exception, though it might |
| 524 | // not be very useful (always pointing to DatabaseUtils#readExceptionFromParcel()). |
| 525 | result.exception = new IllegalStateException("Failed to load root document."); |
| 526 | mInjector.getModel().update(result); |
| 527 | return; |
| 528 | } |
| 529 | |
Tony Huang | 35ad6bd | 2018-11-06 14:39:40 +0800 | [diff] [blame] | 530 | mActivity.getSupportLoaderManager().restartLoader(LOADER_ID, null, mBindings); |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 531 | } |
| 532 | |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 533 | protected final boolean launchToDocument(Uri uri) { |
| 534 | // We don't support launching to a document in an archive. |
Steve McKay | 98f8c5f | 2017-03-03 13:52:14 -0800 | [diff] [blame] | 535 | if (!Providers.isArchiveUri(uri)) { |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 536 | loadDocument(uri, this::onStackLoaded); |
| 537 | return true; |
| 538 | } |
| 539 | |
| 540 | return false; |
| 541 | } |
| 542 | |
| 543 | private void onStackLoaded(@Nullable DocumentStack stack) { |
| 544 | if (stack != null) { |
| 545 | if (!stack.peek().isDirectory()) { |
| 546 | // Requested document is not a directory. Pop it so that we can launch into its |
| 547 | // parent. |
| 548 | stack.pop(); |
| 549 | } |
| 550 | mState.stack.reset(stack); |
| 551 | mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); |
| 552 | |
shawnlin | 9cee68f | 2019-01-25 11:20:18 +0800 | [diff] [blame] | 553 | Metrics.logLaunchAtLocation(mState, stack.getRoot().getUri()); |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 554 | } else { |
| 555 | Log.w(TAG, "Failed to launch into the given uri. Launch to default location."); |
| 556 | launchToDefaultLocation(); |
| 557 | |
shawnlin | 9cee68f | 2019-01-25 11:20:18 +0800 | [diff] [blame] | 558 | Metrics.logLaunchAtLocation(mState, null); |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 559 | } |
| 560 | } |
| 561 | |
Tony Huang | 957be8c | 2019-07-30 15:49:36 +0800 | [diff] [blame] | 562 | private void onRootLoaded(@Nullable RootInfo root) { |
Stephen Hughes | 3bd3e0d | 2019-10-07 10:49:12 +0800 | [diff] [blame] | 563 | boolean invalidRootForAction = |
| 564 | (root != null |
| 565 | && !root.supportsChildren() |
| 566 | && mState.action == State.ACTION_OPEN_TREE); |
| 567 | |
| 568 | if (invalidRootForAction) { |
| 569 | loadDeviceRoot(); |
| 570 | } else if (root != null) { |
Tony Huang | 957be8c | 2019-07-30 15:49:36 +0800 | [diff] [blame] | 571 | mActivity.onRootPicked(root); |
| 572 | } else { |
| 573 | launchToDefaultLocation(); |
| 574 | } |
| 575 | } |
| 576 | |
Garfield Tan | f8969d6 | 2017-02-02 16:55:55 -0800 | [diff] [blame] | 577 | protected abstract void launchToDefaultLocation(); |
| 578 | |
Garfield Tan | 5f2a9ba | 2017-05-26 14:35:44 -0700 | [diff] [blame] | 579 | protected void restoreRootAndDirectory() { |
| 580 | if (!mState.stack.getRoot().isRecents() && mState.stack.isEmpty()) { |
| 581 | mActivity.onRootPicked(mState.stack.getRoot()); |
| 582 | } else { |
| 583 | mActivity.restoreRootAndDirectory(); |
| 584 | } |
| 585 | } |
| 586 | |
Ivan Chiang | 462569a | 2019-09-25 16:16:35 +0800 | [diff] [blame] | 587 | protected final void loadDeviceRoot() { |
| 588 | mActivity.onRootPicked( |
| 589 | mProviders.getRootOneshot(Providers.AUTHORITY_STORAGE, Providers.ROOT_ID_DEVICE)); |
| 590 | } |
| 591 | |
Steve McKay | 988d8a3 | 2016-09-27 09:41:17 -0700 | [diff] [blame] | 592 | protected final void loadHomeDir() { |
| 593 | loadRoot(Shared.getDefaultRootUri(mActivity)); |
| 594 | } |
| 595 | |
Tony Huang | 4e6242a | 2019-01-25 11:45:31 +0800 | [diff] [blame] | 596 | protected final void loadRecent() { |
| 597 | mState.stack.changeRoot(mProviders.getRecentsRoot()); |
| 598 | mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); |
| 599 | } |
| 600 | |
Riddle Hsu | 0c37598 | 2018-06-21 22:06:43 +0800 | [diff] [blame] | 601 | protected MutableSelection<String> getStableSelection() { |
| 602 | MutableSelection<String> selection = new MutableSelection<>(); |
Steve McKay | 2d19a69 | 2017-08-22 09:05:08 -0700 | [diff] [blame] | 603 | mSelectionMgr.copySelection(selection); |
| 604 | return selection; |
Steve McKay | 5b0a2c1 | 2016-10-07 11:22:31 -0700 | [diff] [blame] | 605 | } |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 606 | |
Steve McKay | 710248d | 2017-03-14 10:09:30 -0700 | [diff] [blame] | 607 | @Override |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 608 | public ActionHandler reset(ContentLock reloadLock) { |
| 609 | mContentLock = reloadLock; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 610 | mActivity.getLoaderManager().destroyLoader(LOADER_ID); |
| 611 | return this; |
| 612 | } |
| 613 | |
| 614 | private final class LoaderBindings implements LoaderCallbacks<DirectoryResult> { |
| 615 | |
| 616 | @Override |
| 617 | public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) { |
| 618 | Context context = mActivity; |
| 619 | |
| 620 | if (mState.stack.isRecents()) { |
Tony Huang | 4d179b7 | 2019-08-15 14:25:10 +0800 | [diff] [blame] | 621 | final LockingContentObserver observer = new LockingContentObserver( |
| 622 | mContentLock, AbstractActionHandler.this::loadDocumentsForCurrentStack); |
| 623 | MultiRootDocumentsLoader loader; |
| 624 | |
Ivan Chiang | 282baa4 | 2018-08-08 18:54:14 +0800 | [diff] [blame] | 625 | if (mSearchMgr.isSearching()) { |
| 626 | if (DEBUG) { |
Ivan Chiang | f0ea0ed | 2018-12-12 11:10:01 +0800 | [diff] [blame] | 627 | Log.d(TAG, "Creating new GlobalSearchLoader."); |
Ivan Chiang | 282baa4 | 2018-08-08 18:54:14 +0800 | [diff] [blame] | 628 | } |
Tony Huang | 4d179b7 | 2019-08-15 14:25:10 +0800 | [diff] [blame] | 629 | loader = new GlobalSearchLoader( |
Ivan Chiang | 282baa4 | 2018-08-08 18:54:14 +0800 | [diff] [blame] | 630 | context, |
| 631 | mProviders, |
| 632 | mState, |
| 633 | mExecutors, |
| 634 | mInjector.fileTypeLookup, |
Ivan Chiang | f0ea0ed | 2018-12-12 11:10:01 +0800 | [diff] [blame] | 635 | mSearchMgr.buildQueryArgs()); |
Ivan Chiang | 282baa4 | 2018-08-08 18:54:14 +0800 | [diff] [blame] | 636 | } else { |
| 637 | if (DEBUG) { |
| 638 | Log.d(TAG, "Creating new loader recents."); |
| 639 | } |
Tony Huang | 4d179b7 | 2019-08-15 14:25:10 +0800 | [diff] [blame] | 640 | loader = new RecentsLoader( |
Ivan Chiang | 282baa4 | 2018-08-08 18:54:14 +0800 | [diff] [blame] | 641 | context, |
| 642 | mProviders, |
| 643 | mState, |
| 644 | mExecutors, |
| 645 | mInjector.fileTypeLookup); |
| 646 | } |
Tony Huang | 4d179b7 | 2019-08-15 14:25:10 +0800 | [diff] [blame] | 647 | loader.setObserver(observer); |
| 648 | return loader; |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 649 | } else { |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 650 | Uri contentsUri = mSearchMgr.isSearching() |
| 651 | ? DocumentsContract.buildSearchDocumentsUri( |
| 652 | mState.stack.getRoot().authority, |
| 653 | mState.stack.getRoot().rootId, |
| 654 | mSearchMgr.getCurrentSearch()) |
| 655 | : DocumentsContract.buildChildDocumentsUri( |
| 656 | mState.stack.peek().authority, |
| 657 | mState.stack.peek().documentId); |
| 658 | |
Ivan Chiang | f0ea0ed | 2018-12-12 11:10:01 +0800 | [diff] [blame] | 659 | final Bundle queryArgs = mSearchMgr.isSearching() |
| 660 | ? mSearchMgr.buildQueryArgs() |
| 661 | : null; |
| 662 | |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 663 | if (mInjector.config.managedModeEnabled(mState.stack)) { |
| 664 | contentsUri = DocumentsContract.setManageMode(contentsUri); |
| 665 | } |
| 666 | |
Jason Chang | 96f886b | 2019-03-29 17:59:02 +0800 | [diff] [blame] | 667 | if (DEBUG) { |
| 668 | Log.d(TAG, |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 669 | "Creating new directory loader for: " |
Jason Chang | 96f886b | 2019-03-29 17:59:02 +0800 | [diff] [blame] | 670 | + DocumentInfo.debugString(mState.stack.peek())); |
| 671 | } |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 672 | |
| 673 | return new DirectoryLoader( |
Steve McKay | 710248d | 2017-03-14 10:09:30 -0700 | [diff] [blame] | 674 | mInjector.features, |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 675 | context, |
Tony Huang | 74ded6e | 2018-11-06 17:16:47 +0800 | [diff] [blame] | 676 | mState, |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 677 | contentsUri, |
Garfield Tan | b47b4b5 | 2017-05-16 12:48:53 -0700 | [diff] [blame] | 678 | mInjector.fileTypeLookup, |
Steve McKay | 1239452 | 2017-08-24 14:14:10 -0700 | [diff] [blame] | 679 | mContentLock, |
Ivan Chiang | f0ea0ed | 2018-12-12 11:10:01 +0800 | [diff] [blame] | 680 | queryArgs); |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 681 | } |
| 682 | } |
| 683 | |
| 684 | @Override |
| 685 | public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) { |
Jason Chang | 96f886b | 2019-03-29 17:59:02 +0800 | [diff] [blame] | 686 | if (DEBUG) { |
| 687 | Log.d(TAG, "Loader has finished for: " |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 688 | + DocumentInfo.debugString(mState.stack.peek())); |
Jason Chang | 96f886b | 2019-03-29 17:59:02 +0800 | [diff] [blame] | 689 | } |
Garfield Tan | e967033 | 2017-03-06 18:33:23 -0800 | [diff] [blame] | 690 | assert(result != null); |
| 691 | |
| 692 | mInjector.getModel().update(result); |
| 693 | } |
| 694 | |
| 695 | @Override |
| 696 | public void onLoaderReset(Loader<DirectoryResult> loader) {} |
| 697 | } |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 698 | /** |
| 699 | * A class primarily for the support of isolating our tests |
| 700 | * from our concrete activity implementations. |
| 701 | */ |
| 702 | public interface CommonAddons { |
Garfield Tan | 5f2a9ba | 2017-05-26 14:35:44 -0700 | [diff] [blame] | 703 | void restoreRootAndDirectory(); |
Steve McKay | 0f5ec83 | 2016-10-04 14:21:56 -0700 | [diff] [blame] | 704 | void refreshCurrentRootAndDirectory(@AnimationType int anim); |
| 705 | void onRootPicked(RootInfo root); |
| 706 | // TODO: Move this to PickAddons as multi-document picking is exclusive to that activity. |
| 707 | void onDocumentsPicked(List<DocumentInfo> docs); |
| 708 | void onDocumentPicked(DocumentInfo doc); |
Steve McKay | 0f5ec83 | 2016-10-04 14:21:56 -0700 | [diff] [blame] | 709 | RootInfo getCurrentRoot(); |
| 710 | DocumentInfo getCurrentDirectory(); |
Tony Huang | fdbab1a | 2019-04-08 18:10:27 +0800 | [diff] [blame] | 711 | /** |
| 712 | * Check whether current directory is root of recent. |
| 713 | */ |
| 714 | boolean isInRecents(); |
Garfield Tan | 208945c | 2016-10-04 14:36:38 -0700 | [diff] [blame] | 715 | void setRootsDrawerOpen(boolean open); |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 716 | |
Garfield Tan | 1686883 | 2016-09-26 10:01:45 -0700 | [diff] [blame] | 717 | // TODO: Let navigator listens to State |
| 718 | void updateNavigator(); |
| 719 | |
Garfield Tan | 63bf813 | 2016-10-11 11:00:49 -0700 | [diff] [blame] | 720 | @VisibleForTesting |
| 721 | void notifyDirectoryNavigated(Uri docUri); |
Steve McKay | c8889af | 2016-09-23 11:22:41 -0700 | [diff] [blame] | 722 | } |
Steve McKay | 739f94b | 2016-09-22 14:54:23 -0700 | [diff] [blame] | 723 | } |