DocumentsActivity.java revision 6d97d3c1a2ebac4e3f32c7e5bc134864ace1c17f
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.DocumentsActivity.State.ACTION_CREATE;
20import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
21import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
22import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
23import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
24import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
25import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
26
27import android.app.ActionBar;
28import android.app.ActionBar.OnNavigationListener;
29import android.app.Activity;
30import android.app.Fragment;
31import android.app.FragmentManager;
32import android.content.ActivityNotFoundException;
33import android.content.ClipData;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.ContentValues;
37import android.content.Intent;
38import android.content.pm.ResolveInfo;
39import android.database.Cursor;
40import android.graphics.drawable.ColorDrawable;
41import android.net.Uri;
42import android.os.Bundle;
43import android.os.Parcel;
44import android.provider.DocumentsContract;
45import android.support.v4.app.ActionBarDrawerToggle;
46import android.support.v4.view.GravityCompat;
47import android.support.v4.widget.DrawerLayout;
48import android.support.v4.widget.DrawerLayout.DrawerListener;
49import android.util.Log;
50import android.view.LayoutInflater;
51import android.view.Menu;
52import android.view.MenuItem;
53import android.view.View;
54import android.view.ViewGroup;
55import android.widget.BaseAdapter;
56import android.widget.SearchView;
57import android.widget.SearchView.OnCloseListener;
58import android.widget.SearchView.OnQueryTextListener;
59import android.widget.TextView;
60import android.widget.Toast;
61
62import com.android.documentsui.model.DocumentInfo;
63import com.android.documentsui.model.DocumentStack;
64import com.android.documentsui.model.DurableUtils;
65import com.android.documentsui.model.RootInfo;
66
67import libcore.io.IoUtils;
68
69import java.io.FileNotFoundException;
70import java.io.IOException;
71import java.util.Arrays;
72import java.util.List;
73
74public class DocumentsActivity extends Activity {
75    public static final String TAG = "Documents";
76
77    private SearchView mSearchView;
78
79    private View mRootsContainer;
80    private DrawerLayout mDrawerLayout;
81    private ActionBarDrawerToggle mDrawerToggle;
82
83    private static final String EXTRA_STATE = "state";
84
85    private boolean mIgnoreNextNavigation;
86
87    private RootsCache mRoots;
88    private State mState;
89
90    @Override
91    public void onCreate(Bundle icicle) {
92        super.onCreate(icicle);
93
94        mRoots = DocumentsApplication.getRootsCache(this);
95
96        setResult(Activity.RESULT_CANCELED);
97        setContentView(R.layout.activity);
98
99        mRootsContainer = findViewById(R.id.container_roots);
100
101        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
102
103        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
104                R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
105
106        mDrawerLayout.setDrawerListener(mDrawerListener);
107        mDrawerLayout.setDrawerShadow(R.drawable.ic_drawer_shadow, GravityCompat.START);
108
109        if (icicle != null) {
110            mState = icicle.getParcelable(EXTRA_STATE);
111        } else {
112            buildDefaultState();
113        }
114
115        if (mState.action == ACTION_MANAGE) {
116            mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
117        }
118
119        if (mState.action == ACTION_CREATE) {
120            final String mimeType = getIntent().getType();
121            final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
122            SaveFragment.show(getFragmentManager(), mimeType, title);
123        }
124
125        if (mState.action == ACTION_GET_CONTENT) {
126            final Intent moreApps = new Intent(getIntent());
127            moreApps.setComponent(null);
128            moreApps.setPackage(null);
129            RootsFragment.show(getFragmentManager(), moreApps);
130        } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE) {
131            RootsFragment.show(getFragmentManager(), null);
132        }
133
134        onCurrentDirectoryChanged();
135    }
136
137    private void buildDefaultState() {
138        mState = new State();
139
140        final Intent intent = getIntent();
141        final String action = intent.getAction();
142        if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
143            mState.action = ACTION_OPEN;
144        } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
145            mState.action = ACTION_CREATE;
146        } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
147            mState.action = ACTION_GET_CONTENT;
148        } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
149            mState.action = ACTION_MANAGE;
150        }
151
152        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
153            mState.allowMultiple = intent.getBooleanExtra(
154                    Intent.EXTRA_ALLOW_MULTIPLE, false);
155        }
156
157        if (mState.action == ACTION_MANAGE) {
158            mState.acceptMimes = new String[] { "*/*" };
159            mState.allowMultiple = true;
160        } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
161            mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
162        } else {
163            mState.acceptMimes = new String[] { intent.getType() };
164        }
165
166        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
167        mState.showAdvanced = SettingsActivity.getDisplayAdvancedDevices(this);
168
169        if (mState.action == ACTION_MANAGE) {
170            mState.sortOrder = SORT_ORDER_LAST_MODIFIED;
171        }
172
173        if (mState.action == ACTION_MANAGE) {
174            final Uri uri = intent.getData();
175            final String rootId = DocumentsContract.getRootId(uri);
176            final RootInfo root = mRoots.getRoot(uri.getAuthority(), rootId);
177            if (root != null) {
178                onRootPicked(root, true);
179            } else {
180                Log.w(TAG, "Failed to find root: " + uri);
181                finish();
182            }
183
184        } else {
185            // Restore last stack for calling package
186            // TODO: move into async loader
187            final String packageName = getCallingPackage();
188            final Cursor cursor = getContentResolver()
189                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
190            try {
191                if (cursor.moveToFirst()) {
192                    final byte[] rawStack = cursor.getBlob(
193                            cursor.getColumnIndex(RecentsProvider.COL_PATH));
194                    DurableUtils.readFromArray(rawStack, mState.stack);
195                }
196            } catch (IOException e) {
197                Log.w(TAG, "Failed to resume", e);
198            } finally {
199                IoUtils.closeQuietly(cursor);
200            }
201
202            // If restored root isn't valid, fall back to recents
203            final RootInfo root = getCurrentRoot();
204            final List<RootInfo> matchingRoots = mRoots.getMatchingRoots(mState);
205            if (!matchingRoots.contains(root)) {
206                mState.stack.clear();
207            }
208
209            // Only open drawer when showing recents
210            if (mState.stack.isRecents()) {
211                mDrawerLayout.openDrawer(mRootsContainer);
212            }
213        }
214    }
215
216    @Override
217    public void onStart() {
218        super.onStart();
219
220        if (mState.action == ACTION_MANAGE) {
221            mState.showSize = true;
222        } else {
223            mState.showSize = SettingsActivity.getDisplayFileSize(this);
224        }
225    }
226
227    private DrawerListener mDrawerListener = new DrawerListener() {
228        @Override
229        public void onDrawerSlide(View drawerView, float slideOffset) {
230            mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
231        }
232
233        @Override
234        public void onDrawerOpened(View drawerView) {
235            mDrawerToggle.onDrawerOpened(drawerView);
236            updateActionBar();
237        }
238
239        @Override
240        public void onDrawerClosed(View drawerView) {
241            mDrawerToggle.onDrawerClosed(drawerView);
242            updateActionBar();
243        }
244
245        @Override
246        public void onDrawerStateChanged(int newState) {
247            mDrawerToggle.onDrawerStateChanged(newState);
248        }
249    };
250
251    @Override
252    protected void onPostCreate(Bundle savedInstanceState) {
253        super.onPostCreate(savedInstanceState);
254        mDrawerToggle.syncState();
255    }
256
257    public void updateActionBar() {
258        final ActionBar actionBar = getActionBar();
259
260        actionBar.setDisplayShowHomeEnabled(true);
261
262        if (mState.action == ACTION_MANAGE) {
263            actionBar.setDisplayHomeAsUpEnabled(false);
264            mDrawerToggle.setDrawerIndicatorEnabled(false);
265        } else {
266            actionBar.setDisplayHomeAsUpEnabled(true);
267            mDrawerToggle.setDrawerIndicatorEnabled(true);
268        }
269
270        if (mDrawerLayout.isDrawerOpen(mRootsContainer)) {
271            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
272            actionBar.setIcon(new ColorDrawable());
273
274            if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
275                actionBar.setTitle(R.string.title_open);
276            } else if (mState.action == ACTION_CREATE) {
277                actionBar.setTitle(R.string.title_save);
278            }
279        } else {
280            final RootInfo root = getCurrentRoot();
281            actionBar.setIcon(root != null ? root.loadIcon(this) : null);
282
283            if (mState.stack.size() <= 1) {
284                actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
285                actionBar.setTitle(root.title);
286            } else {
287                mIgnoreNextNavigation = true;
288                actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
289                actionBar.setTitle(null);
290                actionBar.setListNavigationCallbacks(mStackAdapter, mStackListener);
291                actionBar.setSelectedNavigationItem(mStackAdapter.getCount() - 1);
292            }
293        }
294    }
295
296    @Override
297    public boolean onCreateOptionsMenu(Menu menu) {
298        super.onCreateOptionsMenu(menu);
299        getMenuInflater().inflate(R.menu.activity, menu);
300
301        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
302        mSearchView = (SearchView) searchMenu.getActionView();
303        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
304            @Override
305            public boolean onQueryTextSubmit(String query) {
306                mState.currentSearch = query;
307                onCurrentDirectoryChanged();
308                mSearchView.setIconified(true);
309                return true;
310            }
311
312            @Override
313            public boolean onQueryTextChange(String newText) {
314                return false;
315            }
316        });
317
318        mSearchView.setOnCloseListener(new OnCloseListener() {
319            @Override
320            public boolean onClose() {
321                mState.currentSearch = null;
322                onCurrentDirectoryChanged();
323                return false;
324            }
325        });
326
327        return true;
328    }
329
330    @Override
331    public boolean onPrepareOptionsMenu(Menu menu) {
332        super.onPrepareOptionsMenu(menu);
333
334        final FragmentManager fm = getFragmentManager();
335        final DocumentInfo cwd = getCurrentDirectory();
336
337        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
338        final MenuItem search = menu.findItem(R.id.menu_search);
339        final MenuItem sort = menu.findItem(R.id.menu_sort);
340        final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
341        final MenuItem grid = menu.findItem(R.id.menu_grid);
342        final MenuItem list = menu.findItem(R.id.menu_list);
343        final MenuItem settings = menu.findItem(R.id.menu_settings);
344
345        grid.setVisible(mState.mode != MODE_GRID);
346        list.setVisible(mState.mode != MODE_LIST);
347
348        // No sorting in recents
349        sort.setVisible(cwd != null);
350        // Only sort by size when visible
351        sortSize.setVisible(mState.showSize);
352
353        final boolean searchVisible;
354        if (mState.action == ACTION_CREATE) {
355            createDir.setVisible(cwd != null && cwd.isCreateSupported());
356            searchVisible = false;
357
358            // No display options in recent directories
359            if (cwd == null) {
360                grid.setVisible(false);
361                list.setVisible(false);
362            }
363
364            SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
365        } else {
366            createDir.setVisible(false);
367            searchVisible = cwd != null && cwd.isSearchSupported();
368        }
369
370        // TODO: close any search in-progress when hiding
371        search.setVisible(searchVisible);
372
373        settings.setVisible(mState.action != ACTION_MANAGE);
374
375        return true;
376    }
377
378    @Override
379    public boolean onOptionsItemSelected(MenuItem item) {
380        if (mDrawerToggle.onOptionsItemSelected(item)) {
381            return true;
382        }
383
384        final int id = item.getItemId();
385        if (id == android.R.id.home) {
386            onBackPressed();
387            return true;
388        } else if (id == R.id.menu_create_dir) {
389            CreateDirectoryFragment.show(getFragmentManager());
390            return true;
391        } else if (id == R.id.menu_search) {
392            return false;
393        } else if (id == R.id.menu_sort_name) {
394            mState.sortOrder = State.SORT_ORDER_DISPLAY_NAME;
395            updateDisplayState();
396            return true;
397        } else if (id == R.id.menu_sort_date) {
398            mState.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
399            updateDisplayState();
400            return true;
401        } else if (id == R.id.menu_sort_size) {
402            mState.sortOrder = State.SORT_ORDER_SIZE;
403            updateDisplayState();
404            return true;
405        } else if (id == R.id.menu_grid) {
406            // TODO: persist explicit user mode for cwd
407            mState.mode = MODE_GRID;
408            updateDisplayState();
409            invalidateOptionsMenu();
410            return true;
411        } else if (id == R.id.menu_list) {
412            // TODO: persist explicit user mode for cwd
413            mState.mode = MODE_LIST;
414            updateDisplayState();
415            invalidateOptionsMenu();
416            return true;
417        } else if (id == R.id.menu_settings) {
418            startActivity(new Intent(this, SettingsActivity.class));
419            return true;
420        } else {
421            return super.onOptionsItemSelected(item);
422        }
423    }
424
425    @Override
426    public void onBackPressed() {
427        final int size = mState.stack.size();
428        if (size > 1) {
429            mState.stack.pop();
430            onCurrentDirectoryChanged();
431        } else if (size == 1 && !mDrawerLayout.isDrawerOpen(mRootsContainer)) {
432            // TODO: open root drawer once we can capture back key
433            super.onBackPressed();
434        } else {
435            super.onBackPressed();
436        }
437    }
438
439    @Override
440    protected void onSaveInstanceState(Bundle state) {
441        super.onSaveInstanceState(state);
442        state.putParcelable(EXTRA_STATE, mState);
443    }
444
445    @Override
446    protected void onRestoreInstanceState(Bundle state) {
447        super.onRestoreInstanceState(state);
448        updateActionBar();
449    }
450
451    private BaseAdapter mStackAdapter = new BaseAdapter() {
452        @Override
453        public int getCount() {
454            return mState.stack.size();
455        }
456
457        @Override
458        public DocumentInfo getItem(int position) {
459            return mState.stack.get(mState.stack.size() - position - 1);
460        }
461
462        @Override
463        public long getItemId(int position) {
464            return position;
465        }
466
467        @Override
468        public View getView(int position, View convertView, ViewGroup parent) {
469            if (convertView == null) {
470                convertView = LayoutInflater.from(parent.getContext())
471                        .inflate(R.layout.item_title, parent, false);
472            }
473
474            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
475            final DocumentInfo doc = getItem(position);
476
477            if (position == 0) {
478                final RootInfo root = getCurrentRoot();
479                title.setText(root.title);
480            } else {
481                title.setText(doc.displayName);
482            }
483
484            return convertView;
485        }
486
487        @Override
488        public View getDropDownView(int position, View convertView, ViewGroup parent) {
489            if (convertView == null) {
490                convertView = LayoutInflater.from(parent.getContext())
491                        .inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
492            }
493
494            final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
495            final DocumentInfo doc = getItem(position);
496
497            if (position == 0) {
498                final RootInfo root = getCurrentRoot();
499                text1.setText(root.title);
500            } else {
501                text1.setText(doc.displayName);
502            }
503
504            return convertView;
505        }
506    };
507
508    private OnNavigationListener mStackListener = new OnNavigationListener() {
509        @Override
510        public boolean onNavigationItemSelected(int itemPosition, long itemId) {
511            if (mIgnoreNextNavigation) {
512                mIgnoreNextNavigation = false;
513                return false;
514            }
515
516            while (mState.stack.size() > itemPosition + 1) {
517                mState.stack.pop();
518            }
519            onCurrentDirectoryChanged();
520            return true;
521        }
522    };
523
524    public RootInfo getCurrentRoot() {
525        if (mState.stack.size() > 0) {
526            return mState.stack.getRoot(mRoots);
527        } else {
528            return mRoots.getRecentsRoot();
529        }
530    }
531
532    public DocumentInfo getCurrentDirectory() {
533        return mState.stack.peek();
534    }
535
536    public State getDisplayState() {
537        return mState;
538    }
539
540    private void onCurrentDirectoryChanged() {
541        final FragmentManager fm = getFragmentManager();
542        final DocumentInfo cwd = getCurrentDirectory();
543
544        if (cwd == null) {
545            // No directory means recents
546            if (mState.action == ACTION_CREATE) {
547                RecentsCreateFragment.show(fm);
548            } else {
549                DirectoryFragment.showRecentsOpen(fm);
550            }
551        } else {
552            if (mState.currentSearch != null) {
553                // Ongoing search
554                DirectoryFragment.showSearch(fm, cwd.uri, mState.currentSearch);
555            } else {
556                // Normal boring directory
557                DirectoryFragment.showNormal(fm, cwd.uri);
558            }
559        }
560
561        // Forget any replacement target
562        if (mState.action == ACTION_CREATE) {
563            final SaveFragment save = SaveFragment.get(fm);
564            if (save != null) {
565                save.setReplaceTarget(null);
566            }
567        }
568
569        updateActionBar();
570        invalidateOptionsMenu();
571        dumpStack();
572    }
573
574    private void updateDisplayState() {
575        // TODO: handle multiple directory stacks on tablets
576        DirectoryFragment.get(getFragmentManager()).updateDisplayState();
577    }
578
579    public void onStackPicked(DocumentStack stack) {
580        mState.stack = stack;
581        onCurrentDirectoryChanged();
582    }
583
584    public void onRootPicked(RootInfo root, boolean closeDrawer) {
585        // Clear entire backstack and start in new root
586        mState.stack.clear();
587
588        if (!mRoots.isRecentsRoot(root)) {
589            try {
590                final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
591                onDocumentPicked(DocumentInfo.fromUri(getContentResolver(), uri));
592            } catch (FileNotFoundException e) {
593            }
594        } else {
595            onCurrentDirectoryChanged();
596        }
597
598        if (closeDrawer) {
599            mDrawerLayout.closeDrawers();
600        }
601    }
602
603    public void onAppPicked(ResolveInfo info) {
604        final Intent intent = new Intent(getIntent());
605        intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
606        intent.setComponent(new ComponentName(
607                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
608        startActivity(intent);
609        finish();
610    }
611
612    public void onDocumentPicked(DocumentInfo doc) {
613        final FragmentManager fm = getFragmentManager();
614        if (doc.isDirectory()) {
615            // TODO: query display mode user preference for this dir
616            if (doc.isGridPreferred()) {
617                mState.mode = MODE_GRID;
618            } else {
619                mState.mode = MODE_LIST;
620            }
621            mState.stack.push(doc);
622            onCurrentDirectoryChanged();
623        } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
624            // Explicit file picked, return
625            onFinished(doc.uri);
626        } else if (mState.action == ACTION_CREATE) {
627            // Replace selected file
628            SaveFragment.get(fm).setReplaceTarget(doc);
629        } else if (mState.action == ACTION_MANAGE) {
630            // First try managing the document; we expect manager to filter
631            // based on authority, so we don't grant.
632            final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
633            manage.setData(doc.uri);
634
635            try {
636                startActivity(manage);
637            } catch (ActivityNotFoundException ex) {
638                // Fall back to viewing
639                final Intent view = new Intent(Intent.ACTION_VIEW);
640                view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
641                view.setData(doc.uri);
642
643                try {
644                    startActivity(view);
645                } catch (ActivityNotFoundException ex2) {
646                    Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
647                }
648            }
649        }
650    }
651
652    public void onDocumentsPicked(List<DocumentInfo> docs) {
653        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
654            final int size = docs.size();
655            final Uri[] uris = new Uri[size];
656            for (int i = 0; i < size; i++) {
657                uris[i] = docs.get(i).uri;
658            }
659            onFinished(uris);
660        }
661    }
662
663    public void onSaveRequested(DocumentInfo replaceTarget) {
664        onFinished(replaceTarget.uri);
665    }
666
667    public void onSaveRequested(String mimeType, String displayName) {
668        final DocumentInfo cwd = getCurrentDirectory();
669        final String authority = cwd.uri.getAuthority();
670
671        final Uri childUri = DocumentsContract.createDocument(
672                getContentResolver(), cwd.uri, mimeType, displayName);
673        if (childUri != null) {
674            onFinished(childUri);
675        } else {
676            Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
677        }
678    }
679
680    private void onFinished(Uri... uris) {
681        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
682
683        final ContentResolver resolver = getContentResolver();
684        final ContentValues values = new ContentValues();
685
686        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
687        if (mState.action == ACTION_CREATE) {
688            // Remember stack for last create
689            values.clear();
690            values.put(RecentsProvider.COL_PATH, rawStack);
691            resolver.insert(RecentsProvider.buildRecentCreate(), values);
692
693        } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
694            // Remember opened items
695            for (Uri uri : uris) {
696                values.clear();
697                values.put(RecentsProvider.COL_URI, uri.toString());
698                resolver.insert(RecentsProvider.buildRecentOpen(), values);
699            }
700        }
701
702        // Remember location for next app launch
703        final String packageName = getCallingPackage();
704        values.clear();
705        values.put(RecentsProvider.COL_PATH, rawStack);
706        resolver.insert(RecentsProvider.buildResume(packageName), values);
707
708        final Intent intent = new Intent();
709        if (uris.length == 1) {
710            intent.setData(uris[0]);
711        } else if (uris.length > 1) {
712            final ClipData clipData = new ClipData(
713                    null, mState.acceptMimes, new ClipData.Item(uris[0]));
714            for (int i = 1; i < uris.length; i++) {
715                clipData.addItem(new ClipData.Item(uris[i]));
716            }
717            intent.setClipData(clipData);
718        }
719
720        if (mState.action == ACTION_GET_CONTENT) {
721            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
722        } else {
723            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
724                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
725                    | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
726        }
727
728        setResult(Activity.RESULT_OK, intent);
729        finish();
730    }
731
732    public static class State implements android.os.Parcelable {
733        public int action;
734        public int mode = MODE_LIST;
735        public String[] acceptMimes;
736        public int sortOrder = SORT_ORDER_DISPLAY_NAME;
737        public boolean allowMultiple = false;
738        public boolean showSize = false;
739        public boolean localOnly = false;
740        public boolean showAdvanced = false;
741
742        /** Current user navigation stack; empty implies recents. */
743        public DocumentStack stack = new DocumentStack();
744        /** Currently active search, overriding any stack. */
745        public String currentSearch;
746
747        public static final int ACTION_OPEN = 1;
748        public static final int ACTION_CREATE = 2;
749        public static final int ACTION_GET_CONTENT = 3;
750        public static final int ACTION_MANAGE = 4;
751
752        public static final int MODE_LIST = 0;
753        public static final int MODE_GRID = 1;
754
755        public static final int SORT_ORDER_DISPLAY_NAME = 0;
756        public static final int SORT_ORDER_LAST_MODIFIED = 1;
757        public static final int SORT_ORDER_SIZE = 2;
758
759        @Override
760        public int describeContents() {
761            return 0;
762        }
763
764        @Override
765        public void writeToParcel(Parcel out, int flags) {
766            out.writeInt(action);
767            out.writeInt(mode);
768            out.writeStringArray(acceptMimes);
769            out.writeInt(sortOrder);
770            out.writeInt(allowMultiple ? 1 : 0);
771            out.writeInt(showSize ? 1 : 0);
772            out.writeInt(localOnly ? 1 : 0);
773            out.writeInt(showAdvanced ? 1 : 0);
774            DurableUtils.writeToParcel(out, stack);
775            out.writeString(currentSearch);
776        }
777
778        public static final Creator<State> CREATOR = new Creator<State>() {
779            @Override
780            public State createFromParcel(Parcel in) {
781                final State state = new State();
782                state.action = in.readInt();
783                state.mode = in.readInt();
784                state.acceptMimes = in.readStringArray();
785                state.sortOrder = in.readInt();
786                state.allowMultiple = in.readInt() != 0;
787                state.showSize = in.readInt() != 0;
788                state.localOnly = in.readInt() != 0;
789                state.showAdvanced = in.readInt() != 0;
790                DurableUtils.readFromParcel(in, state.stack);
791                state.currentSearch = in.readString();
792                return state;
793            }
794
795            @Override
796            public State[] newArray(int size) {
797                return new State[size];
798            }
799        };
800    }
801
802    private void dumpStack() {
803        Log.d(TAG, "Current stack:");
804        for (DocumentInfo doc : mState.stack) {
805            Log.d(TAG, "--> " + doc);
806        }
807    }
808
809    public static DocumentsActivity get(Fragment fragment) {
810        return (DocumentsActivity) fragment.getActivity();
811    }
812}
813