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