DocumentsActivity.java revision 0f7078f0045f354ba6573f0094e097a9afd6534d
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
17package com.android.documentsui;
18
19import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
20import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
21import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
22import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
23import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
24import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
25import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
26import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
27
28import android.app.Activity;
29import android.app.Fragment;
30import android.app.FragmentManager;
31import android.content.ActivityNotFoundException;
32import android.content.ClipData;
33import android.content.ComponentName;
34import android.content.ContentProviderClient;
35import android.content.ContentResolver;
36import android.content.ContentValues;
37import android.content.Context;
38import android.content.Intent;
39import android.content.pm.ResolveInfo;
40import android.content.res.Resources;
41import android.graphics.Point;
42import android.net.Uri;
43import android.os.AsyncTask;
44import android.os.Bundle;
45import android.os.Parcelable;
46import android.provider.DocumentsContract;
47import android.provider.DocumentsContract.Root;
48import android.util.Log;
49import android.view.Menu;
50import android.view.MenuItem;
51import android.view.View;
52import android.view.WindowManager;
53import android.widget.BaseAdapter;
54import android.widget.Spinner;
55import android.widget.Toast;
56import android.widget.Toolbar;
57
58import com.android.documentsui.RecentsProvider.RecentColumns;
59import com.android.documentsui.RecentsProvider.ResumeColumns;
60import com.android.documentsui.model.DocumentInfo;
61import com.android.documentsui.model.DurableUtils;
62import com.android.documentsui.model.RootInfo;
63
64import java.util.Arrays;
65import java.util.List;
66
67public class DocumentsActivity extends BaseActivity {
68    private static final int CODE_FORWARD = 42;
69    private static final String TAG = "DocumentsActivity";
70
71    private boolean mShowAsDialog;
72
73    private Toolbar mToolbar;
74    private Spinner mToolbarStack;
75
76    private Toolbar mRootsToolbar;
77
78    private DirectoryContainerView mDirectoryContainer;
79
80    private ItemSelectedListener mStackListener;
81    private BaseAdapter mStackAdapter;
82
83    public DocumentsActivity() {
84        super(R.layout.docs_activity, TAG);
85    }
86
87    @Override
88    public void onCreate(Bundle icicle) {
89        super.onCreate(icicle);
90
91        final Resources res = getResources();
92        mShowAsDialog = res.getBoolean(R.bool.show_as_dialog) && mState.action != ACTION_BROWSE;
93
94        if (!mShowAsDialog) {
95            setTheme(R.style.DocumentsNonDialogTheme);
96        }
97
98        final Context context = this;
99
100        if (mShowAsDialog) {
101            mDrawer = DrawerController.createDummy();
102
103            // Strongly define our horizontal dimension; we leave vertical as
104            // WRAP_CONTENT so that system resizes us when IME is showing.
105            final WindowManager.LayoutParams a = getWindow().getAttributes();
106
107            final Point size = new Point();
108            getWindowManager().getDefaultDisplay().getSize(size);
109            a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
110
111            getWindow().setAttributes(a);
112
113        } else {
114            mDrawer = DrawerController.create(this);
115        }
116
117        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
118
119        mToolbar = (Toolbar) findViewById(R.id.toolbar);
120
121        mStackAdapter = new StackAdapter();
122        mStackListener = new ItemSelectedListener();
123        mToolbarStack = (Spinner) findViewById(R.id.stack);
124        mToolbarStack.setOnItemSelectedListener(mStackListener);
125
126        mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
127
128        setActionBar(mToolbar);
129
130        // Hide roots when we're managing a specific root
131        if (mState.action == ACTION_BROWSE) {
132            mDrawer.lockClosed();
133            if (mShowAsDialog) {
134                findViewById(R.id.container_roots).setVisibility(View.GONE);
135            }
136        }
137
138        if (mState.action == ACTION_CREATE) {
139            final String mimeType = getIntent().getType();
140            final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
141            SaveFragment.show(getFragmentManager(), mimeType, title);
142        } else if (mState.action == ACTION_OPEN_TREE ||
143                   mState.action == ACTION_OPEN_COPY_DESTINATION) {
144            PickFragment.show(getFragmentManager());
145        }
146
147        if (mState.action == ACTION_GET_CONTENT) {
148            final Intent moreApps = new Intent(getIntent());
149            moreApps.setComponent(null);
150            moreApps.setPackage(null);
151            RootsFragment.show(getFragmentManager(), moreApps);
152        } else if (mState.action == ACTION_OPEN ||
153                   mState.action == ACTION_CREATE ||
154                   mState.action == ACTION_OPEN_TREE ||
155                   mState.action == ACTION_OPEN_COPY_DESTINATION) {
156            RootsFragment.show(getFragmentManager(), null);
157        }
158
159        if (!mState.restored) {
160            // In this case, we set the activity title in AsyncTask.onPostExecute().  To prevent
161            // talkback from reading aloud the default title, we clear it here.
162            setTitle("");
163            if (mState.action == ACTION_BROWSE) {
164                final Uri rootUri = getIntent().getData();
165                new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
166            } else {
167                new RestoreStackTask().execute();
168            }
169        } else {
170            onCurrentDirectoryChanged(ANIM_NONE);
171        }
172    }
173
174    @Override
175    State buildState() {
176        State state = buildDefaultState();
177
178        final Intent intent = getIntent();
179        final String action = intent.getAction();
180        if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
181            state.action = ACTION_OPEN;
182        } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
183            state.action = ACTION_CREATE;
184        } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
185            state.action = ACTION_GET_CONTENT;
186        } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
187            state.action = ACTION_OPEN_TREE;
188        } else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
189            state.action = ACTION_BROWSE;
190        } else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
191            state.action = ACTION_OPEN_COPY_DESTINATION;
192        }
193
194        if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
195            state.allowMultiple = intent.getBooleanExtra(
196                    Intent.EXTRA_ALLOW_MULTIPLE, false);
197        }
198
199        if (state.action == ACTION_BROWSE) {
200            state.acceptMimes = new String[] { "*/*" };
201            state.allowMultiple = true;
202        } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
203            state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
204        } else {
205            state.acceptMimes = new String[] { intent.getType() };
206        }
207
208        if (state.action == ACTION_BROWSE) {
209            state.showSize = true;
210        } else {
211            state.showSize = LocalPreferences.getDisplayFileSize(this);
212        }
213        if (state.action == ACTION_OPEN_COPY_DESTINATION) {
214            state.directoryCopy = intent.getBooleanExtra(
215                    BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
216            state.transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
217                    CopyService.TRANSFER_MODE_NONE);
218        }
219
220        return state;
221    }
222
223    @Override
224    void onStackRestored(boolean restored, boolean external) {
225        // Show drawer when no stack restored, but only when requesting
226        // non-visual content. However, if we last used an external app,
227        // drawer is always shown.
228
229        boolean showDrawer = false;
230        if (!restored) {
231            showDrawer = true;
232        }
233        if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
234            showDrawer = false;
235        }
236        if (external && mState.action == ACTION_GET_CONTENT) {
237            showDrawer = true;
238        }
239
240        if (showDrawer) {
241            setRootsDrawerOpen(true);
242        }
243    }
244
245    public void onAppPicked(ResolveInfo info) {
246        final Intent intent = new Intent(getIntent());
247        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
248        intent.setComponent(new ComponentName(
249                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
250        startActivityForResult(intent, CODE_FORWARD);
251    }
252
253    @Override
254    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
255        Log.d(TAG, "onActivityResult() code=" + resultCode);
256
257        // Only relay back results when not canceled; otherwise stick around to
258        // let the user pick another app/backend.
259        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
260
261            // Remember that we last picked via external app
262            final String packageName = getCallingPackageMaybeExtra();
263            final ContentValues values = new ContentValues();
264            values.put(ResumeColumns.EXTERNAL, 1);
265            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
266
267            // Pass back result to original caller
268            setResult(resultCode, data);
269            finish();
270        } else {
271            super.onActivityResult(requestCode, resultCode, data);
272        }
273    }
274
275    @Override
276    protected void onPostCreate(Bundle savedInstanceState) {
277        super.onPostCreate(savedInstanceState);
278        mDrawer.syncState();
279        updateActionBar();
280    }
281
282    public void setRootsDrawerOpen(boolean open) {
283        mDrawer.setOpen(open);
284    }
285
286    @Override
287    public void updateActionBar() {
288        if (mRootsToolbar != null) {
289            final String prompt = getIntent().getStringExtra(DocumentsContract.EXTRA_PROMPT);
290            if (prompt != null) {
291                mRootsToolbar.setTitle(prompt);
292            } else {
293                if (mState.action == ACTION_OPEN ||
294                    mState.action == ACTION_GET_CONTENT ||
295                    mState.action == ACTION_OPEN_TREE) {
296                    mRootsToolbar.setTitle(R.string.title_open);
297                } else if (mState.action == ACTION_CREATE ||
298                           mState.action == ACTION_OPEN_COPY_DESTINATION) {
299                    mRootsToolbar.setTitle(R.string.title_save);
300                }
301            }
302        }
303
304        if (!mShowAsDialog && mDrawer.isUnlocked()) {
305            mToolbar.setNavigationIcon(R.drawable.ic_hamburger);
306            mToolbar.setNavigationContentDescription(R.string.drawer_open);
307            mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
308                @Override
309                public void onClick(View v) {
310                    setRootsDrawerOpen(true);
311                }
312            });
313        } else {
314            mToolbar.setNavigationIcon(null);
315            mToolbar.setNavigationContentDescription(R.string.drawer_open);
316            mToolbar.setNavigationOnClickListener(null);
317        }
318
319        if (mSearchManager.isExpanded()) {
320            mToolbar.setTitle(null);
321            mToolbarStack.setVisibility(View.GONE);
322            mToolbarStack.setAdapter(null);
323        } else {
324            if (mState.stack.size() <= 1) {
325                mToolbar.setTitle(getCurrentRoot().title);
326                mToolbarStack.setVisibility(View.GONE);
327                mToolbarStack.setAdapter(null);
328            } else {
329                mToolbar.setTitle(null);
330                mToolbarStack.setVisibility(View.VISIBLE);
331                mToolbarStack.setAdapter(mStackAdapter);
332
333                mStackListener.mIgnoreNextNavigation = true;
334                mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
335            }
336        }
337    }
338
339    @Override
340    public boolean onCreateOptionsMenu(Menu menu) {
341        boolean showMenu = super.onCreateOptionsMenu(menu);
342
343        // Most actions are visible when showing as dialog
344        if (mShowAsDialog) {
345            expandMenus(menu);
346        }
347        return showMenu;
348    }
349
350    @Override
351    public boolean onPrepareOptionsMenu(Menu menu) {
352        super.onPrepareOptionsMenu(menu);
353
354        final RootInfo root = getCurrentRoot();
355        final DocumentInfo cwd = getCurrentDirectory();
356
357        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
358        final MenuItem grid = menu.findItem(R.id.menu_grid);
359        final MenuItem list = menu.findItem(R.id.menu_list);
360        final MenuItem advanced = menu.findItem(R.id.menu_advanced);
361        final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
362        final MenuItem settings = menu.findItem(R.id.menu_settings);
363
364        // File size is locked visible for browse because that is the action triggered by Settings,
365        // where the user is trying to find large files to clean up.
366        // TODO: instead of setting this according to the action, use a local preference, but
367        // provide a @hide extra to let callers like Settings force-enable size visibility.
368        boolean fileSizeVisible = mState.action != ACTION_BROWSE;
369        if (mState.action == ACTION_CREATE
370                || mState.action == ACTION_OPEN_TREE
371                || mState.action == ACTION_OPEN_COPY_DESTINATION) {
372            createDir.setVisible(cwd != null && cwd.isCreateSupported());
373            mSearchManager.showMenu(false);
374
375            // No display options in recent directories
376            if (cwd == null) {
377                grid.setVisible(false);
378                list.setVisible(false);
379                fileSizeVisible = false;
380            }
381
382            if (mState.action == ACTION_CREATE) {
383                final FragmentManager fm = getFragmentManager();
384                SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
385            }
386        } else {
387            createDir.setVisible(false);
388        }
389
390        advanced.setVisible(mState.action != ACTION_BROWSE && !mState.forceAdvanced);
391        fileSize.setVisible(fileSizeVisible);
392
393        settings.setVisible(mState.action == ACTION_BROWSE
394                && (root.flags & Root.FLAG_HAS_SETTINGS) != 0);
395
396        return true;
397    }
398
399    @Override
400    public boolean onOptionsItemSelected(MenuItem item) {
401        return mDrawer.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
402    }
403
404    @Override
405    void onDirectoryChanged(int anim) {
406        final FragmentManager fm = getFragmentManager();
407        final RootInfo root = getCurrentRoot();
408        final DocumentInfo cwd = getCurrentDirectory();
409
410        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
411
412        if (cwd == null) {
413            // No directory means recents
414            if (mState.action == ACTION_CREATE ||
415                mState.action == ACTION_OPEN_TREE ||
416                mState.action == ACTION_OPEN_COPY_DESTINATION) {
417                RecentsCreateFragment.show(fm);
418            } else {
419                DirectoryFragment.showRecentsOpen(fm, anim);
420
421                // Start recents in grid when requesting visual things
422                final boolean visualMimes = MimePredicate.mimeMatches(
423                        MimePredicate.VISUAL_MIMES, mState.acceptMimes);
424                mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
425                mState.derivedMode = mState.userMode;
426            }
427        } else {
428            if (mState.currentSearch != null) {
429                // Ongoing search
430                DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
431            } else {
432                // Normal boring directory
433                DirectoryFragment.showNormal(fm, root, cwd, anim);
434            }
435        }
436
437        // Forget any replacement target
438        if (mState.action == ACTION_CREATE) {
439            final SaveFragment save = SaveFragment.get(fm);
440            if (save != null) {
441                save.setReplaceTarget(null);
442            }
443        }
444
445        if (mState.action == ACTION_OPEN_TREE ||
446            mState.action == ACTION_OPEN_COPY_DESTINATION) {
447            final PickFragment pick = PickFragment.get(fm);
448            if (pick != null) {
449                pick.setPickTarget(mState.action, mState.transferMode, cwd);
450            }
451        }
452    }
453
454    void onSaveRequested(DocumentInfo replaceTarget) {
455        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
456    }
457
458    void onSaveRequested(String mimeType, String displayName) {
459        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
460    }
461
462    @Override
463    void onRootPicked(RootInfo root) {
464        super.onRootPicked(root);
465        setRootsDrawerOpen(false);
466    }
467
468    @Override
469    public void onDocumentPicked(DocumentInfo doc, DocumentContext context) {
470        final FragmentManager fm = getFragmentManager();
471        if (doc.isDirectory()) {
472            openDirectory(doc);
473        } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
474            // Explicit file picked, return
475            new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
476        } else if (mState.action == ACTION_CREATE) {
477            // Replace selected file
478            SaveFragment.get(fm).setReplaceTarget(doc);
479        } else if (mState.action == ACTION_BROWSE) {
480            // Go straight to viewing
481            final Intent view = new Intent(Intent.ACTION_VIEW);
482            view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
483            view.setData(doc.derivedUri);
484
485            try {
486                startActivity(view);
487            } catch (ActivityNotFoundException ex) {
488                Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
489            }
490        }
491    }
492
493    @Override
494    public void onDocumentsPicked(List<DocumentInfo> docs) {
495        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
496            final int size = docs.size();
497            final Uri[] uris = new Uri[size];
498            for (int i = 0; i < size; i++) {
499                uris[i] = docs.get(i).derivedUri;
500            }
501            new ExistingFinishTask(uris).executeOnExecutor(getCurrentExecutor());
502        }
503    }
504
505    public void onPickRequested(DocumentInfo pickTarget) {
506        Uri result;
507        if (mState.action == ACTION_OPEN_TREE) {
508            result = DocumentsContract.buildTreeDocumentUri(
509                    pickTarget.authority, pickTarget.documentId);
510        } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
511            result = pickTarget.derivedUri;
512        } else {
513            // Should not be reached.
514            throw new IllegalStateException("Invalid mState.action.");
515        }
516        new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
517    }
518
519    @Override
520    void saveStackBlocking() {
521        final ContentResolver resolver = getContentResolver();
522        final ContentValues values = new ContentValues();
523
524        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
525        if (mState.action == ACTION_CREATE ||
526            mState.action == ACTION_OPEN_TREE ||
527            mState.action == ACTION_OPEN_COPY_DESTINATION) {
528            // Remember stack for last create
529            values.clear();
530            values.put(RecentColumns.KEY, mState.stack.buildKey());
531            values.put(RecentColumns.STACK, rawStack);
532            resolver.insert(RecentsProvider.buildRecent(), values);
533        }
534
535        // Remember location for next app launch
536        final String packageName = getCallingPackageMaybeExtra();
537        values.clear();
538        values.put(ResumeColumns.STACK, rawStack);
539        values.put(ResumeColumns.EXTERNAL, 0);
540        resolver.insert(RecentsProvider.buildResume(packageName), values);
541    }
542
543    @Override
544    void onTaskFinished(Uri... uris) {
545        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
546
547        final Intent intent = new Intent();
548        if (uris.length == 1) {
549            intent.setData(uris[0]);
550        } else if (uris.length > 1) {
551            final ClipData clipData = new ClipData(
552                    null, mState.acceptMimes, new ClipData.Item(uris[0]));
553            for (int i = 1; i < uris.length; i++) {
554                clipData.addItem(new ClipData.Item(uris[i]));
555            }
556            intent.setClipData(clipData);
557        }
558
559        if (mState.action == ACTION_GET_CONTENT) {
560            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
561        } else if (mState.action == ACTION_OPEN_TREE) {
562            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
563                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
564                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
565                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
566        } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
567            // Picking a copy destination is only used internally by us, so we
568            // don't need to extend permissions to the caller.
569            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
570            intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
571        } else {
572            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
573                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
574                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
575        }
576
577        setResult(Activity.RESULT_OK, intent);
578        finish();
579    }
580
581    public static DocumentsActivity get(Fragment fragment) {
582        return (DocumentsActivity) fragment.getActivity();
583    }
584
585    private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
586        private final Uri mUri;
587
588        public PickFinishTask(Uri uri) {
589            mUri = uri;
590        }
591
592        @Override
593        protected Void doInBackground(Void... params) {
594            saveStackBlocking();
595            return null;
596        }
597
598        @Override
599        protected void onPostExecute(Void result) {
600            onTaskFinished(mUri);
601        }
602    }
603
604    final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
605        private final Uri[] mUris;
606
607        public ExistingFinishTask(Uri... uris) {
608            mUris = uris;
609        }
610
611        @Override
612        protected Void doInBackground(Void... params) {
613            saveStackBlocking();
614            return null;
615        }
616
617        @Override
618        protected void onPostExecute(Void result) {
619            onTaskFinished(mUris);
620        }
621    }
622
623    /**
624     * Task that creates a new document in the background.
625     */
626    final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
627        private final String mMimeType;
628        private final String mDisplayName;
629
630        public CreateFinishTask(String mimeType, String displayName) {
631            mMimeType = mimeType;
632            mDisplayName = displayName;
633        }
634
635        @Override
636        protected void onPreExecute() {
637            setPending(true);
638        }
639
640        @Override
641        protected Uri doInBackground(Void... params) {
642            final ContentResolver resolver = getContentResolver();
643            final DocumentInfo cwd = getCurrentDirectory();
644
645            ContentProviderClient client = null;
646            Uri childUri = null;
647            try {
648                client = DocumentsApplication.acquireUnstableProviderOrThrow(
649                        resolver, cwd.derivedUri.getAuthority());
650                childUri = DocumentsContract.createDocument(
651                        client, cwd.derivedUri, mMimeType, mDisplayName);
652            } catch (Exception e) {
653                Log.w(TAG, "Failed to create document", e);
654            } finally {
655                ContentProviderClient.releaseQuietly(client);
656            }
657
658            if (childUri != null) {
659                saveStackBlocking();
660            }
661
662            return childUri;
663        }
664
665        @Override
666        protected void onPostExecute(Uri result) {
667            if (result != null) {
668                onTaskFinished(result);
669            } else {
670                Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
671                        .show();
672            }
673
674            setPending(false);
675        }
676    }
677}
678