DocumentsActivity.java revision 4eb407a832b7d6a2d62a535e5cab70b00a0bc8ed
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.DisplayState.ACTION_CREATE;
20import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_GET_CONTENT;
21import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_MANAGE;
22import static com.android.documentsui.DocumentsActivity.DisplayState.ACTION_OPEN;
23import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_GRID;
24import static com.android.documentsui.DocumentsActivity.DisplayState.MODE_LIST;
25import static com.android.documentsui.DocumentsActivity.DisplayState.SORT_ORDER_DATE;
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.provider.DocumentsContract;
44import android.provider.DocumentsContract.DocumentColumns;
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.Document;
63import com.android.documentsui.model.DocumentStack;
64import com.android.documentsui.model.Root;
65
66import java.io.FileNotFoundException;
67import java.util.Arrays;
68import java.util.List;
69
70public class DocumentsActivity extends Activity {
71    public static final String TAG = "Documents";
72
73    private int mAction;
74
75    private SearchView mSearchView;
76
77    private View mRootsContainer;
78    private DrawerLayout mDrawerLayout;
79    private ActionBarDrawerToggle mDrawerToggle;
80
81    private final DisplayState mDisplayState = new DisplayState();
82
83    private RootsCache mRoots;
84
85    /** Current user navigation stack; empty implies recents. */
86    private DocumentStack mStack = new DocumentStack();
87    /** Currently active search, overriding any stack. */
88    private String mCurrentSearch;
89
90    @Override
91    public void onCreate(Bundle icicle) {
92        super.onCreate(icicle);
93
94        mRoots = DocumentsApplication.getRootsCache(this);
95
96        final Intent intent = getIntent();
97        final String action = intent.getAction();
98        if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
99            mAction = ACTION_OPEN;
100        } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
101            mAction = ACTION_CREATE;
102        } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
103            mAction = ACTION_GET_CONTENT;
104        } else if (Intent.ACTION_MANAGE_DOCUMENT.equals(action)) {
105            mAction = ACTION_MANAGE;
106        }
107
108        // TODO: unify action in single place
109        mDisplayState.action = mAction;
110
111        if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
112            mDisplayState.allowMultiple = intent.getBooleanExtra(
113                    Intent.EXTRA_ALLOW_MULTIPLE, false);
114        }
115
116        if (mAction == ACTION_MANAGE) {
117            mDisplayState.acceptMimes = new String[] { "*/*" };
118            mDisplayState.allowMultiple = true;
119        } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
120            mDisplayState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
121        } else {
122            mDisplayState.acceptMimes = new String[] { intent.getType() };
123        }
124
125        mDisplayState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
126
127        setResult(Activity.RESULT_CANCELED);
128        setContentView(R.layout.activity);
129
130        if (mAction == ACTION_CREATE) {
131            final String mimeType = getIntent().getType();
132            final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
133            SaveFragment.show(getFragmentManager(), mimeType, title);
134        }
135
136        if (mAction == ACTION_GET_CONTENT) {
137            final Intent moreApps = new Intent(getIntent());
138            moreApps.setComponent(null);
139            moreApps.setPackage(null);
140            RootsFragment.show(getFragmentManager(), moreApps);
141        } else if (mAction == ACTION_OPEN || mAction == ACTION_CREATE) {
142            RootsFragment.show(getFragmentManager(), null);
143        }
144
145        if (mAction == ACTION_MANAGE) {
146            mDisplayState.sortOrder = SORT_ORDER_DATE;
147        }
148
149        mRootsContainer = findViewById(R.id.container_roots);
150
151        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
152
153        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
154                R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
155
156        mDrawerLayout.setDrawerListener(mDrawerListener);
157        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
158
159        if (mAction == ACTION_MANAGE) {
160            mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
161
162            final Uri rootUri = intent.getData();
163            final String authority = rootUri.getAuthority();
164            final String rootId = DocumentsContract.getRootId(rootUri);
165
166            final Root root = mRoots.findRoot(authority, rootId);
167            if (root != null) {
168                onRootPicked(root, true);
169            } else {
170                Log.w(TAG, "Failed to find root: " + rootUri);
171                finish();
172            }
173
174        } else {
175            mDrawerLayout.openDrawer(mRootsContainer);
176
177            // Restore last stack for calling package
178            // TODO: move into async loader
179            final String packageName = getCallingPackage();
180            final Cursor cursor = getContentResolver()
181                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
182            try {
183                if (cursor.moveToFirst()) {
184                    final String raw = cursor.getString(
185                            cursor.getColumnIndex(RecentsProvider.COL_PATH));
186                    mStack = DocumentStack.deserialize(getContentResolver(), raw);
187                }
188            } catch (FileNotFoundException e) {
189                Log.w(TAG, "Failed to resume", e);
190            } finally {
191                cursor.close();
192            }
193
194            onCurrentDirectoryChanged();
195        }
196    }
197
198    @Override
199    public void onStart() {
200        super.onStart();
201
202        if (mAction == ACTION_MANAGE) {
203            mDisplayState.showSize = true;
204        } else {
205            mDisplayState.showSize = SettingsActivity.getDisplayFileSize(this);
206        }
207    }
208
209    private DrawerListener mDrawerListener = new DrawerListener() {
210        @Override
211        public void onDrawerSlide(View drawerView, float slideOffset) {
212            mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
213        }
214
215        @Override
216        public void onDrawerOpened(View drawerView) {
217            mDrawerToggle.onDrawerOpened(drawerView);
218            updateActionBar();
219        }
220
221        @Override
222        public void onDrawerClosed(View drawerView) {
223            mDrawerToggle.onDrawerClosed(drawerView);
224            updateActionBar();
225        }
226
227        @Override
228        public void onDrawerStateChanged(int newState) {
229            mDrawerToggle.onDrawerStateChanged(newState);
230        }
231    };
232
233    @Override
234    protected void onPostCreate(Bundle savedInstanceState) {
235        super.onPostCreate(savedInstanceState);
236        mDrawerToggle.syncState();
237    }
238
239    public void updateActionBar() {
240        final ActionBar actionBar = getActionBar();
241
242        actionBar.setDisplayShowHomeEnabled(true);
243
244        if (mDrawerLayout.isDrawerOpen(mRootsContainer)) {
245            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
246            actionBar.setIcon(new ColorDrawable());
247
248            if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
249                actionBar.setTitle(R.string.title_open);
250            } else if (mAction == ACTION_CREATE) {
251                actionBar.setTitle(R.string.title_save);
252            }
253
254            actionBar.setDisplayHomeAsUpEnabled(true);
255            mDrawerToggle.setDrawerIndicatorEnabled(true);
256
257        } else {
258            final Root root = getCurrentRoot();
259            actionBar.setIcon(root != null ? root.icon : null);
260
261            if (root.isRecents) {
262                actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
263                actionBar.setTitle(root.title);
264            } else {
265                actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
266                actionBar.setTitle(null);
267                actionBar.setListNavigationCallbacks(mSortAdapter, mSortListener);
268                actionBar.setSelectedNavigationItem(mDisplayState.sortOrder);
269            }
270
271            if (mStack.size() > 1) {
272                actionBar.setDisplayHomeAsUpEnabled(true);
273                mDrawerToggle.setDrawerIndicatorEnabled(false);
274            } else if (mAction == ACTION_MANAGE) {
275                actionBar.setDisplayHomeAsUpEnabled(false);
276                mDrawerToggle.setDrawerIndicatorEnabled(false);
277            } else {
278                actionBar.setDisplayHomeAsUpEnabled(true);
279                mDrawerToggle.setDrawerIndicatorEnabled(true);
280            }
281        }
282    }
283
284    @Override
285    public boolean onCreateOptionsMenu(Menu menu) {
286        super.onCreateOptionsMenu(menu);
287        getMenuInflater().inflate(R.menu.activity, menu);
288
289        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
290        mSearchView = (SearchView) searchMenu.getActionView();
291        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
292            @Override
293            public boolean onQueryTextSubmit(String query) {
294                mCurrentSearch = query;
295                onCurrentDirectoryChanged();
296                mSearchView.setIconified(true);
297                return true;
298            }
299
300            @Override
301            public boolean onQueryTextChange(String newText) {
302                return false;
303            }
304        });
305
306        mSearchView.setOnCloseListener(new OnCloseListener() {
307            @Override
308            public boolean onClose() {
309                mCurrentSearch = null;
310                onCurrentDirectoryChanged();
311                return false;
312            }
313        });
314
315        return true;
316    }
317
318    @Override
319    public boolean onPrepareOptionsMenu(Menu menu) {
320        super.onPrepareOptionsMenu(menu);
321
322        final FragmentManager fm = getFragmentManager();
323        final Document cwd = getCurrentDirectory();
324
325        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
326        final MenuItem search = menu.findItem(R.id.menu_search);
327        final MenuItem grid =  menu.findItem(R.id.menu_grid);
328        final MenuItem list = menu.findItem(R.id.menu_list);
329        final MenuItem settings = menu.findItem(R.id.menu_settings);
330
331        grid.setVisible(mDisplayState.mode != MODE_GRID);
332        list.setVisible(mDisplayState.mode != MODE_LIST);
333
334        final boolean searchVisible;
335        if (mAction == ACTION_CREATE) {
336            createDir.setVisible(cwd != null && cwd.isCreateSupported());
337            searchVisible = false;
338
339            // No display options in recent directories
340            if (cwd == null) {
341                grid.setVisible(false);
342                list.setVisible(false);
343            }
344
345            SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
346        } else {
347            createDir.setVisible(false);
348            searchVisible = cwd != null && cwd.isSearchSupported();
349        }
350
351        // TODO: close any search in-progress when hiding
352        search.setVisible(searchVisible);
353
354        settings.setVisible(mAction != ACTION_MANAGE);
355
356        return true;
357    }
358
359    @Override
360    public boolean onOptionsItemSelected(MenuItem item) {
361        if (mDrawerToggle.onOptionsItemSelected(item)) {
362            return true;
363        }
364
365        final int id = item.getItemId();
366        if (id == android.R.id.home) {
367            onBackPressed();
368            return true;
369        } else if (id == R.id.menu_create_dir) {
370            CreateDirectoryFragment.show(getFragmentManager());
371            return true;
372        } else if (id == R.id.menu_search) {
373            return false;
374        } else if (id == R.id.menu_grid) {
375            // TODO: persist explicit user mode for cwd
376            mDisplayState.mode = MODE_GRID;
377            updateDisplayState();
378            invalidateOptionsMenu();
379            return true;
380        } else if (id == R.id.menu_list) {
381            // TODO: persist explicit user mode for cwd
382            mDisplayState.mode = MODE_LIST;
383            updateDisplayState();
384            invalidateOptionsMenu();
385            return true;
386        } else if (id == R.id.menu_settings) {
387            startActivity(new Intent(this, SettingsActivity.class));
388            return true;
389        } else {
390            return super.onOptionsItemSelected(item);
391        }
392    }
393
394    @Override
395    public void onBackPressed() {
396        final int size = mStack.size();
397        if (size > 1) {
398            mStack.pop();
399            onCurrentDirectoryChanged();
400        } else if (size == 1 && !mDrawerLayout.isDrawerOpen(mRootsContainer)) {
401            // TODO: open root drawer once we can capture back key
402            super.onBackPressed();
403        } else {
404            super.onBackPressed();
405        }
406    }
407
408    // TODO: support additional sort orders
409    private BaseAdapter mSortAdapter = new BaseAdapter() {
410        @Override
411        public int getCount() {
412            return mDisplayState.showSize ? 3 : 2;
413        }
414
415        @Override
416        public Object getItem(int position) {
417            switch (position) {
418                case 0:
419                    return getText(R.string.sort_name);
420                case 1:
421                    return getText(R.string.sort_date);
422                case 2:
423                    return getText(R.string.sort_size);
424                default:
425                    return null;
426            }
427        }
428
429        @Override
430        public long getItemId(int position) {
431            return position;
432        }
433
434        @Override
435        public View getView(int position, View convertView, ViewGroup parent) {
436            if (convertView == null) {
437                convertView = LayoutInflater.from(parent.getContext())
438                        .inflate(R.layout.item_title, parent, false);
439            }
440
441            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
442            final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
443
444            final Document cwd = getCurrentDirectory();
445            if (cwd != null) {
446                title.setText(cwd.displayName);
447            } else {
448                // No directory means recents
449                title.setText(R.string.root_recent);
450            }
451
452            summary.setText((String) getItem(position));
453
454            return convertView;
455        }
456
457        @Override
458        public View getDropDownView(int position, View convertView, ViewGroup parent) {
459            if (convertView == null) {
460                convertView = LayoutInflater.from(parent.getContext())
461                        .inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
462            }
463
464            final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
465            text1.setText((String) getItem(position));
466
467            return convertView;
468        }
469    };
470
471    private OnNavigationListener mSortListener = new OnNavigationListener() {
472        @Override
473        public boolean onNavigationItemSelected(int itemPosition, long itemId) {
474            mDisplayState.sortOrder = itemPosition;
475            updateDisplayState();
476            return true;
477        }
478    };
479
480    public Root getCurrentRoot() {
481        final Document cwd = getCurrentDirectory();
482        if (cwd != null) {
483            return mRoots.findRoot(cwd);
484        } else {
485            return mRoots.getRecentsRoot();
486        }
487    }
488
489    public Document getCurrentDirectory() {
490        return mStack.peek();
491    }
492
493    public DisplayState getDisplayState() {
494        return mDisplayState;
495    }
496
497    private void onCurrentDirectoryChanged() {
498        final FragmentManager fm = getFragmentManager();
499        final Document cwd = getCurrentDirectory();
500
501        if (cwd == null) {
502            // No directory means recents
503            if (mAction == ACTION_CREATE) {
504                RecentsCreateFragment.show(fm);
505            } else {
506                DirectoryFragment.showRecentsOpen(fm);
507            }
508        } else {
509            if (mCurrentSearch != null) {
510                // Ongoing search
511                DirectoryFragment.showSearch(fm, cwd.uri, mCurrentSearch);
512            } else {
513                // Normal boring directory
514                DirectoryFragment.showNormal(fm, cwd.uri);
515            }
516        }
517
518        // Forget any replacement target
519        if (mAction == ACTION_CREATE) {
520            final SaveFragment save = SaveFragment.get(fm);
521            if (save != null) {
522                save.setReplaceTarget(null);
523            }
524        }
525
526        updateActionBar();
527        invalidateOptionsMenu();
528        dumpStack();
529    }
530
531    private void updateDisplayState() {
532        // TODO: handle multiple directory stacks on tablets
533        DirectoryFragment.get(getFragmentManager()).updateDisplayState();
534    }
535
536    public void onStackPicked(DocumentStack stack) {
537        mStack = stack;
538        onCurrentDirectoryChanged();
539    }
540
541    public void onRootPicked(Root root, boolean closeDrawer) {
542        // Clear entire backstack and start in new root
543        mStack.clear();
544
545        if (!root.isRecents) {
546            try {
547                onDocumentPicked(Document.fromRoot(getContentResolver(), root));
548            } catch (FileNotFoundException e) {
549            }
550        } else {
551            onCurrentDirectoryChanged();
552        }
553
554        if (closeDrawer) {
555            mDrawerLayout.closeDrawers();
556        }
557    }
558
559    public void onAppPicked(ResolveInfo info) {
560        final Intent intent = new Intent(getIntent());
561        intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
562        intent.setComponent(new ComponentName(
563                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
564        startActivity(intent);
565        finish();
566    }
567
568    public void onDocumentPicked(Document doc) {
569        final FragmentManager fm = getFragmentManager();
570        if (doc.isDirectory()) {
571            // TODO: query display mode user preference for this dir
572            if (doc.isGridPreferred()) {
573                mDisplayState.mode = MODE_GRID;
574            } else {
575                mDisplayState.mode = MODE_LIST;
576            }
577            mStack.push(doc);
578            onCurrentDirectoryChanged();
579        } else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
580            // Explicit file picked, return
581            onFinished(doc.uri);
582        } else if (mAction == ACTION_CREATE) {
583            // Replace selected file
584            SaveFragment.get(fm).setReplaceTarget(doc);
585        } else if (mAction == ACTION_MANAGE) {
586            // Open the document
587            // TODO: trampoline activity for launching downloaded APKs
588            final Intent intent = new Intent(Intent.ACTION_VIEW);
589            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
590            intent.setData(doc.uri);
591            try {
592                startActivity(intent);
593            } catch (ActivityNotFoundException ex) {
594                Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
595            }
596        }
597    }
598
599    public void onDocumentsPicked(List<Document> docs) {
600        if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
601            final int size = docs.size();
602            final Uri[] uris = new Uri[size];
603            for (int i = 0; i < size; i++) {
604                uris[i] = docs.get(i).uri;
605            }
606            onFinished(uris);
607        }
608    }
609
610    public void onSaveRequested(Document replaceTarget) {
611        onFinished(replaceTarget.uri);
612    }
613
614    public void onSaveRequested(String mimeType, String displayName) {
615        final ContentValues values = new ContentValues();
616        values.put(DocumentColumns.MIME_TYPE, mimeType);
617        values.put(DocumentColumns.DISPLAY_NAME, displayName);
618
619        final Document cwd = getCurrentDirectory();
620        final Uri childUri = getContentResolver().insert(cwd.uri, values);
621        if (childUri != null) {
622            onFinished(childUri);
623        } else {
624            Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
625        }
626    }
627
628    private void onFinished(Uri... uris) {
629        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
630
631        final ContentResolver resolver = getContentResolver();
632        final ContentValues values = new ContentValues();
633
634        final String rawStack = DocumentStack.serialize(mStack);
635        if (mAction == ACTION_CREATE) {
636            // Remember stack for last create
637            values.clear();
638            values.put(RecentsProvider.COL_PATH, rawStack);
639            resolver.insert(RecentsProvider.buildRecentCreate(), values);
640
641        } else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
642            // Remember opened items
643            for (Uri uri : uris) {
644                values.clear();
645                values.put(RecentsProvider.COL_URI, uri.toString());
646                resolver.insert(RecentsProvider.buildRecentOpen(), values);
647            }
648        }
649
650        // Remember location for next app launch
651        final String packageName = getCallingPackage();
652        values.clear();
653        values.put(RecentsProvider.COL_PATH, rawStack);
654        resolver.insert(RecentsProvider.buildResume(packageName), values);
655
656        final Intent intent = new Intent();
657        if (uris.length == 1) {
658            intent.setData(uris[0]);
659        } else if (uris.length > 1) {
660            final ClipData clipData = new ClipData(
661                    null, mDisplayState.acceptMimes, new ClipData.Item(uris[0]));
662            for (int i = 1; i < uris.length; i++) {
663                clipData.addItem(new ClipData.Item(uris[i]));
664            }
665            intent.setClipData(clipData);
666        }
667
668        if (mAction == ACTION_GET_CONTENT) {
669            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
670        } else {
671            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
672                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
673                    | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
674        }
675
676        setResult(Activity.RESULT_OK, intent);
677        finish();
678    }
679
680    public static class DisplayState {
681        public int action;
682        public int mode = MODE_LIST;
683        public String[] acceptMimes;
684        public int sortOrder = SORT_ORDER_NAME;
685        public boolean allowMultiple = false;
686        public boolean showSize = false;
687        public boolean localOnly = false;
688
689        public static final int ACTION_OPEN = 1;
690        public static final int ACTION_CREATE = 2;
691        public static final int ACTION_GET_CONTENT = 3;
692        public static final int ACTION_MANAGE = 4;
693
694        public static final int MODE_LIST = 0;
695        public static final int MODE_GRID = 1;
696
697        public static final int SORT_ORDER_NAME = 0;
698        public static final int SORT_ORDER_DATE = 1;
699        public static final int SORT_ORDER_SIZE = 2;
700    }
701
702    private void dumpStack() {
703        Log.d(TAG, "Current stack:");
704        for (Document doc : mStack) {
705            Log.d(TAG, "--> " + doc);
706        }
707    }
708
709    public static DocumentsActivity get(Fragment fragment) {
710        return (DocumentsActivity) fragment.getActivity();
711    }
712}
713