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