• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.documentsui.picker;
18 
19 import static com.android.documentsui.base.State.ACTION_CREATE;
20 import static com.android.documentsui.base.State.ACTION_GET_CONTENT;
21 import static com.android.documentsui.base.State.ACTION_OPEN;
22 import static com.android.documentsui.base.State.ACTION_OPEN_TREE;
23 import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION;
24 
25 import android.app.Fragment;
26 import android.app.FragmentManager;
27 import android.content.Intent;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.provider.DocumentsContract;
31 import android.support.annotation.CallSuper;
32 import android.view.KeyEvent;
33 import android.view.Menu;
34 
35 import com.android.documentsui.ActionModeController;
36 import com.android.documentsui.BaseActivity;
37 import com.android.documentsui.DocsSelectionHelper;
38 import com.android.documentsui.DocumentsApplication;
39 import com.android.documentsui.FocusManager;
40 import com.android.documentsui.Injector;
41 import com.android.documentsui.MenuManager.DirectoryDetails;
42 import com.android.documentsui.ProviderExecutor;
43 import com.android.documentsui.R;
44 import com.android.documentsui.SharedInputHandler;
45 import com.android.documentsui.base.DocumentInfo;
46 import com.android.documentsui.base.Features;
47 import com.android.documentsui.base.MimeTypes;
48 import com.android.documentsui.base.RootInfo;
49 import com.android.documentsui.base.Shared;
50 import com.android.documentsui.base.State;
51 import com.android.documentsui.dirlist.DirectoryFragment;
52 import com.android.documentsui.prefs.ScopedPreferences;
53 import com.android.documentsui.services.FileOperationService;
54 import com.android.documentsui.sidebar.RootsFragment;
55 import com.android.documentsui.ui.DialogController;
56 import com.android.documentsui.ui.MessageBuilder;
57 
58 import java.util.Collection;
59 import java.util.List;
60 
61 public class PickActivity extends BaseActivity implements ActionHandler.Addons {
62 
63     static final String PREFERENCES_SCOPE = "picker";
64 
65     private static final String TAG = "PickActivity";
66 
67     private Injector<ActionHandler<PickActivity>> mInjector;
68     private SharedInputHandler mSharedInputHandler;
69 
70     private LastAccessedStorage mLastAccessed;
71 
PickActivity()72     public PickActivity() {
73         super(R.layout.documents_activity, TAG);
74     }
75 
76     // make these methods visible in this package to work around compiler bug http://b/62218600
focusSidebar()77     @Override protected boolean focusSidebar() { return super.focusSidebar(); }
popDir()78     @Override protected boolean popDir() { return super.popDir(); }
79 
80     @Override
onCreate(Bundle icicle)81     public void onCreate(Bundle icicle) {
82 
83         Features features = Features.create(this);
84         ScopedPreferences prefs = ScopedPreferences.create(this, PREFERENCES_SCOPE);
85 
86         mInjector = new Injector<>(
87                 features,
88                 new Config(),
89                 prefs,
90                 new MessageBuilder(this),
91                 DialogController.create(features, this, null),
92                 DocumentsApplication.getFileTypeLookup(this),
93                 (Collection<RootInfo> roots) -> {});
94 
95         super.onCreate(icicle);
96 
97         mInjector.selectionMgr = mState.allowMultiple
98                 ? DocsSelectionHelper.createMultiSelect()
99                 : DocsSelectionHelper.createSingleSelect();
100 
101         mInjector.focusManager = new FocusManager(
102                 mInjector.features,
103                 mInjector.selectionMgr,
104                 mDrawer,
105                 this::focusSidebar,
106                 getColor(R.color.accent_dark));
107 
108         mInjector.menuManager = new MenuManager(mSearchManager, mState, new DirectoryDetails(this));
109 
110         mInjector.actionModeController = new ActionModeController(
111                 this,
112                 mInjector.selectionMgr,
113                 mInjector.menuManager,
114                 mInjector.messages);
115 
116         mLastAccessed = LastAccessedStorage.create();
117         mInjector.actions = new ActionHandler<>(
118                 this,
119                 mState,
120                 mProviders,
121                 mDocs,
122                 mSearchManager,
123                 ProviderExecutor::forAuthority,
124                 mInjector,
125                 mLastAccessed);
126 
127         mInjector.searchManager = mSearchManager;
128 
129         Intent intent = getIntent();
130 
131         mSharedInputHandler =
132                 new SharedInputHandler(
133                         mInjector.focusManager,
134                         mInjector.selectionMgr,
135                         mInjector.searchManager::cancelSearch,
136                         this::popDir,
137                         mInjector.features);
138         setupLayout(intent);
139         mInjector.actions.initLocation(intent);
140     }
141 
setupLayout(Intent intent)142     private void setupLayout(Intent intent) {
143         if (mState.action == ACTION_CREATE) {
144             final String mimeType = intent.getType();
145             final String title = intent.getStringExtra(Intent.EXTRA_TITLE);
146             SaveFragment.show(getFragmentManager(), mimeType, title);
147         } else if (mState.action == ACTION_OPEN_TREE ||
148                    mState.action == ACTION_PICK_COPY_DESTINATION) {
149             PickFragment.show(getFragmentManager());
150         }
151 
152         if (mState.action == ACTION_GET_CONTENT) {
153             final Intent moreApps = new Intent(intent);
154             moreApps.setComponent(null);
155             moreApps.setPackage(null);
156             RootsFragment.show(getFragmentManager(), moreApps);
157         } else if (mState.action == ACTION_OPEN ||
158                    mState.action == ACTION_CREATE ||
159                    mState.action == ACTION_OPEN_TREE ||
160                    mState.action == ACTION_PICK_COPY_DESTINATION) {
161             RootsFragment.show(getFragmentManager(), (Intent) null);
162         }
163     }
164 
165     @Override
includeState(State state)166     protected void includeState(State state) {
167         final Intent intent = getIntent();
168 
169         String defaultMimeType = (intent.getType() == null) ? "*/*" : intent.getType();
170         state.initAcceptMimes(intent, defaultMimeType);
171 
172         final String action = intent.getAction();
173         if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
174             state.action = ACTION_OPEN;
175         } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
176             state.action = ACTION_CREATE;
177         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
178             state.action = ACTION_GET_CONTENT;
179         } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
180             state.action = ACTION_OPEN_TREE;
181         } else if (Shared.ACTION_PICK_COPY_DESTINATION.equals(action)) {
182             state.action = ACTION_PICK_COPY_DESTINATION;
183         }
184 
185         if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
186             state.allowMultiple = intent.getBooleanExtra(
187                     Intent.EXTRA_ALLOW_MULTIPLE, false);
188         }
189 
190         if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT
191                 || state.action == ACTION_CREATE) {
192             state.openableOnly = intent.hasCategory(Intent.CATEGORY_OPENABLE);
193         }
194 
195         if (state.action == ACTION_PICK_COPY_DESTINATION) {
196             // Indicates that a copy operation (or move) includes a directory.
197             // Why? Directory creation isn't supported by some roots (like Downloads).
198             // This allows us to restrict available roots to just those with support.
199             state.directoryCopy = intent.getBooleanExtra(
200                     Shared.EXTRA_DIRECTORY_COPY, false);
201             state.copyOperationSubType = intent.getIntExtra(
202                     FileOperationService.EXTRA_OPERATION_TYPE,
203                     FileOperationService.OPERATION_COPY);
204         }
205     }
206 
207     @Override
onPostCreate(Bundle savedInstanceState)208     protected void onPostCreate(Bundle savedInstanceState) {
209         super.onPostCreate(savedInstanceState);
210         mDrawer.update();
211         mNavigator.update();
212     }
213 
214     @Override
getDrawerTitle()215     public String getDrawerTitle() {
216         String title = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
217         if (title == null) {
218             if (mState.action == ACTION_OPEN ||
219                 mState.action == ACTION_GET_CONTENT ||
220                 mState.action == ACTION_OPEN_TREE) {
221                 title = getResources().getString(R.string.title_open);
222             } else if (mState.action == ACTION_CREATE ||
223                        mState.action == ACTION_PICK_COPY_DESTINATION) {
224                 title = getResources().getString(R.string.title_save);
225             } else {
226                 // If all else fails, just call it "Documents".
227                 title = getResources().getString(R.string.app_label);
228             }
229         }
230 
231         return title;
232     }
233 
234     @Override
onPrepareOptionsMenu(Menu menu)235     public boolean onPrepareOptionsMenu(Menu menu) {
236         super.onPrepareOptionsMenu(menu);
237         mInjector.menuManager.updateOptionMenu(menu);
238 
239         final DocumentInfo cwd = getCurrentDirectory();
240 
241         if (mState.action == ACTION_CREATE) {
242             final FragmentManager fm = getFragmentManager();
243             SaveFragment.get(fm).prepareForDirectory(cwd);
244         }
245 
246         return true;
247     }
248 
249     @Override
refreshDirectory(int anim)250     protected void refreshDirectory(int anim) {
251         final FragmentManager fm = getFragmentManager();
252         final RootInfo root = getCurrentRoot();
253         final DocumentInfo cwd = getCurrentDirectory();
254 
255         if (mState.stack.isRecents()) {
256             DirectoryFragment.showRecentsOpen(fm, anim);
257 
258             // In recents we pick layout mode based on the mimetype,
259             // picking GRID for visual types. We intentionally don't
260             // consult a user's saved preferences here since they are
261             // set per root (not per root and per mimetype).
262             boolean visualMimes = MimeTypes.mimeMatches(
263                     MimeTypes.VISUAL_MIMES, mState.acceptMimes);
264             mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
265         } else {
266                 // Normal boring directory
267                 DirectoryFragment.showDirectory(fm, root, cwd, anim);
268         }
269 
270         // Forget any replacement target
271         if (mState.action == ACTION_CREATE) {
272             final SaveFragment save = SaveFragment.get(fm);
273             if (save != null) {
274                 save.setReplaceTarget(null);
275             }
276         }
277 
278         if (mState.action == ACTION_OPEN_TREE ||
279             mState.action == ACTION_PICK_COPY_DESTINATION) {
280             final PickFragment pick = PickFragment.get(fm);
281             if (pick != null) {
282                 pick.setPickTarget(mState.action, mState.copyOperationSubType, cwd);
283             }
284         }
285     }
286 
287     @Override
onDirectoryCreated(DocumentInfo doc)288     protected void onDirectoryCreated(DocumentInfo doc) {
289         assert(doc.isDirectory());
290         mInjector.actions.openContainerDocument(doc);
291     }
292 
293     @Override
onDocumentPicked(DocumentInfo doc)294     public void onDocumentPicked(DocumentInfo doc) {
295         final FragmentManager fm = getFragmentManager();
296         // Do not inline-open archives, as otherwise it would be impossible to pick
297         // archive files. Note, that picking files inside archives is not supported.
298         if (doc.isDirectory()) {
299             mInjector.actions.openContainerDocument(doc);
300         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
301             // Explicit file picked, return
302             mInjector.actions.finishPicking(doc.derivedUri);
303         } else if (mState.action == ACTION_CREATE) {
304             // Replace selected file
305             SaveFragment.get(fm).setReplaceTarget(doc);
306         }
307     }
308 
309     @Override
onDocumentsPicked(List<DocumentInfo> docs)310     public void onDocumentsPicked(List<DocumentInfo> docs) {
311         if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
312             final int size = docs.size();
313             final Uri[] uris = new Uri[size];
314             for (int i = 0; i < size; i++) {
315                 uris[i] = docs.get(i).derivedUri;
316             }
317             mInjector.actions.finishPicking(uris);
318         }
319     }
320 
321     @CallSuper
322     @Override
onKeyDown(int keyCode, KeyEvent event)323     public boolean onKeyDown(int keyCode, KeyEvent event) {
324         return mSharedInputHandler.onKeyDown(
325                 keyCode,
326                 event)
327                 || super.onKeyDown(keyCode, event);
328     }
329 
330     @Override
setResult(int resultCode, Intent intent, int notUsed)331     public void setResult(int resultCode, Intent intent, int notUsed) {
332         setResult(resultCode, intent);
333     }
334 
get(Fragment fragment)335     public static PickActivity get(Fragment fragment) {
336         return (PickActivity) fragment.getActivity();
337     }
338 
339     @Override
getInjector()340     public Injector<ActionHandler<PickActivity>> getInjector() {
341         return mInjector;
342     }
343 }
344