Home | History | Annotate | Download | only in providers
      1 page.title=  
      2 @jd:body
      3 <div id="qv-wrapper">
      4 <div id="qv">
      5 
      6 <h2>
      7  <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle">
      8         <span class="more"></span>
      9         <span class="less" style="display:none"></span></a></h2>
     10 <ol id="toc44" class="hide-nested">
     11     <li>
     12         <a href="#overview"></a>
     13     </li>
     14     <li>
     15         <a href="#flow"> </a>
     16     </li>
     17     <li>
     18         <a href="#client"> </a>
     19         <ol>
     20         <li><a href="#search"></a></li>
     21         <li><a href="#process"></a></li>
     22         <li><a href="#metadata"> </a></li>
     23         <li><a href="#open"></a></li>
     24         <li><a href="#create"></a></li>
     25         <li><a href="#delete"></a></li>
     26         <li><a href="#edit"></a></li>
     27         <li><a href="#permissions"></a></li>
     28         </ol>
     29     </li>
     30     <li><a href="#custom">  </a>
     31         <ol>
     32         <li><a href="#manifest"></a></li>
     33         <li><a href="#contract"></a></li>
     34         <li><a href="#subclass"> DocumentsProvider</a></li>
     35         <li><a href="#security"></a></li>
     36         </ol>
     37     </li>
     38 
     39 </ol>
     40 <h2></h2>
     41 <ol>
     42     <li>{@link android.provider.DocumentsProvider}</li>
     43     <li>{@link android.provider.DocumentsContract}</li>
     44 </ol>
     45 
     46 <h2></h2>
     47 
     48 <ol>
     49     <li><a href="http://www.youtube.com/watch?v=zxHVeXbK1P4">
     50 DevBytes: Android 4.4   : </a></li>
     51      <li><a href="http://www.youtube.com/watch?v=UFj9AEz0DHQ">
     52 DevBytes: Android 4.4   : </a></li>
     53 </ol>
     54 
     55 
     56 <h2></h2>
     57 
     58 <ol>
     59     <li><a href="{@docRoot}samples/StorageProvider/index.html">
     60 StorageProvider</a></li>
     61      <li><a href="{@docRoot}samples/StorageClient/index.html">
     62 StorageClient</a></li>
     63 </ol>
     64 
     65 <h2></h2>
     66 <ol>
     67     <li>
     68         <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">                 </a>
     69 
     70 
     71     </li>
     72 </ol>
     73 
     74 </div>
     75 </div>
     76 
     77 
     78 <p>Android 4.4 API  19  SAFSAF   
     79 
     80  UI 
     81 </p>
     82 
     83 <p> {@link android.provider.DocumentsProvider}   
     84  SAF 
     85 
     86 </p>
     87 
     88 <p>SAF </p>
     89 
     90 <ul>
     91 <li><strong> </strong>&mdash; Google  
     92   {@link android.provider.DocumentsProvider} document-provider  Android  
     93 
     94 
     95 
     96 
     97 </li>
     98 
     99 <li><strong> </strong>&mdash;{@link android.content.Intent#ACTION_OPEN_DOCUMENT}  {@link android.content.Intent#ACTION_CREATE_DOCUMENT}  
    100 
    101 
    102 </li>
    103 
    104 <li><strong></strong>&mdash;  UI 
    105 </li>
    106 </ul>
    107 
    108 <p>SAF </p>
    109 <ul>
    110 <li> 1  </li>
    111 <li> 
    112 
    113 </li>
    114 <li>  USB  
    115  </li>
    116 </ul>
    117 
    118 <h2 id ="overview"></h2>
    119 
    120 <p>SAF {@link android.provider.DocumentsProvider}  
    121  <em></em>
    122 </p>
    123 <p><img src="{@docRoot}images/providers/storage_datamodel.png" alt="data model" /></p>
    124 <p class="img-caption"><strong> 1.</strong>   1 
    125 </p>
    126 
    127 <p>: </p>
    128 <ul>
    129 
    130 <li>  1  {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID}  USB  
    131 
    132 
    133 
    134 
    135 
    136 </li>
    137 
    138 <li> 1 1  <em>N</em>  1  <em>N</em> 
    139  </li>
    140 
    141 <li>  {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID}  URI  ID 
    142 
    143 
    144 
    145 </li>
    146 
    147 
    148 <li> MIME {@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR} MIME 
    149 
    150 </li>
    151 
    152 <li>{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE}{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}  {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 
    153 
    154 
    155 
    156 
    157 
    158 </li>
    159 </ul>
    160 
    161 <h2 id="flow"> </h2>
    162 <p> 
    163 {@link android.provider.DocumentsProvider} API  
    164 
    165 </p>
    166 
    167 <p> 2  SAF 
    168 </p>
    169 <p><img src="{@docRoot}images/providers/storage_dataflow.png" alt="app" /></p>
    170 
    171 <p class="img-caption"><strong> 2.</strong>    </p>
    172 
    173 <p>: </p>
    174 <ul>
    175 
    176 <li>SAF 
    177 
    178 </li>
    179 
    180 <li> {@link android.content.Intent#ACTION_OPEN_DOCUMENT}  {@link android.content.Intent#ACTION_CREATE_DOCUMENT} 
    181 &mdash; MIME 
    182 
    183 </li>
    184 
    185 <li> 
    186 </li>
    187 
    188 <li> 
    189  2 Google Drive USB  
    190 </li>
    191 </ul>
    192 
    193 <p> 3 Google Drive 
    194 </p>
    195 
    196 <p><img src="{@docRoot}images/providers/storage_picker.png" width="340" alt="picker" style="border:2px solid #ddd" /></p>
    197 
    198 <p class="img-caption"><strong> 3.</strong> </p>
    199 
    200 <p> Google Drive  4 
    201  
    202 
    203 
    204 <p><img src="{@docRoot}images/providers/storage_photos.png" width="340" alt="picker" style="border:2px solid #ddd" /></p>
    205 
    206 <p class="img-caption"><strong> 4.</strong></p>
    207 
    208 <h2 id="client"> </h2>
    209 
    210 <p>Android 4.3 {@link android.content.Intent#ACTION_PICK}  {@link android.content.Intent#ACTION_GET_CONTENT} 
    211 
    212  1  
    213 
    214  </p>
    215 
    216 <p>Android 4.4 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}  UI 
    217 
    218 
    219  1  UI 
    220 </p>
    221 
    222 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT} {@link android.content.Intent#ACTION_GET_CONTENT} 
    223 
    224 </p>
    225 
    226 <ul>
    227 <li>{@link android.content.Intent#ACTION_GET_CONTENT} 
    228 
    229 </li>
    230 
    231 <li> {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 
    232 
    233  
    234  </li>
    235 
    236 </ul>
    237 
    238 
    239 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT}  {@link android.content.Intent#ACTION_CREATE_DOCUMENT}  
    240 
    241 </p>
    242 
    243 
    244 <h3 id="search"></h3>
    245 
    246 <p>
    247  {@link android.content.Intent#ACTION_OPEN_DOCUMENT}  
    248 
    249 </p>
    250 
    251 <pre>private static final int READ_REQUEST_CODE = 42;
    252 ...
    253 /**
    254  * Fires an intent to spin up the &quot;file chooser&quot; UI and select an image.
    255  */
    256 public void performFileSearch() {
    257 
    258     // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    259     // browser.
    260     Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    261 
    262     // Filter to only show results that can be &quot;opened&quot;, such as a
    263     // file (as opposed to a list of contacts or timezones)
    264     intent.addCategory(Intent.CATEGORY_OPENABLE);
    265 
    266     // Filter to show only images, using the image MIME data type.
    267     // If one wanted to search for ogg vorbis files, the type would be &quot;audio/ogg&quot;.
    268     // To search for all documents available via installed storage providers,
    269     // it would be &quot;*/*&quot;.
    270     intent.setType(&quot;image/*&quot;);
    271 
    272     startActivityForResult(intent, READ_REQUEST_CODE);
    273 }</pre>
    274 
    275 <p>: </p>
    276 <ul>
    277 <li> {@link android.content.Intent#ACTION_OPEN_DOCUMENT}  
    278 </li>
    279 
    280 <li> {@link android.content.Intent#CATEGORY_OPENABLE} 
    281 </li>
    282 
    283 <li>{@code intent.setType("image/*")}  MIME 
    284 </li>
    285 </ul>
    286 
    287 <h3 id="results"></h3>
    288 
    289 <p>{@link android.app.Activity#onActivityResult onActivityResult()}  URI  {@code resultData} 
    290 
    291 
    292 {@link android.content.Intent#getData getData()}  URI URI  URI 
    293 
    294 </p>
    295 
    296 <pre>&#64;Override
    297 public void onActivityResult(int requestCode, int resultCode,
    298         Intent resultData) {
    299 
    300     // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    301     // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    302     // response to some other intent, and the code below shouldn't run at all.
    303 
    304     if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    305         // The document selected by the user won't be returned in the intent.
    306         // Instead, a URI to that document will be contained in the return intent
    307         // provided to this method as a parameter.
    308         // Pull that URI using resultData.getData().
    309         Uri uri = null;
    310         if (resultData != null) {
    311             uri = resultData.getData();
    312             Log.i(TAG, "Uri: " + uri.toString());
    313             showImage(uri);
    314         }
    315     }
    316 }
    317 </pre>
    318 
    319 <h3 id="metadata"> </h3>
    320 
    321 <p>URI URI 
    322 </p>
    323 
    324 <pre>public void dumpImageMetaData(Uri uri) {
    325 
    326     // The query, since it only applies to a single document, will only return
    327     // one row. There's no need to filter, sort, or select fields, since we want
    328     // all fields for one document.
    329     Cursor cursor = getActivity().getContentResolver()
    330             .query(uri, null, null, null, null, null);
    331 
    332     try {
    333     // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    334     // &quot;if there's anything to look at, look at it&quot; conditionals.
    335         if (cursor != null &amp;&amp; cursor.moveToFirst()) {
    336 
    337             // Note it's called &quot;Display Name&quot;.  This is
    338             // provider-specific, and might not necessarily be the file name.
    339             String displayName = cursor.getString(
    340                     cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    341             Log.i(TAG, &quot;Display Name: &quot; + displayName);
    342 
    343             int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
    344             // If the size is unknown, the value stored is null.  But since an
    345             // int can't be null in Java, the behavior is implementation-specific,
    346             // which is just a fancy term for &quot;unpredictable&quot;.  So as
    347             // a rule, check if it's null before assigning to an int.  This will
    348             // happen often:  The storage API allows for remote files, whose
    349             // size might not be locally known.
    350             String size = null;
    351             if (!cursor.isNull(sizeIndex)) {
    352                 // Technically the column stores an int, but cursor.getString()
    353                 // will do the conversion automatically.
    354                 size = cursor.getString(sizeIndex);
    355             } else {
    356                 size = &quot;Unknown&quot;;
    357             }
    358             Log.i(TAG, &quot;Size: &quot; + size);
    359         }
    360     } finally {
    361         cursor.close();
    362     }
    363 }
    364 </pre>
    365 
    366 <h3 id="open-client"></h3>
    367 
    368 <p>URI 
    369 </p>
    370 
    371 <h4></h4>
    372 
    373 <p>{@link android.graphics.Bitmap} </p>
    374 
    375 <pre>private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    376     ParcelFileDescriptor parcelFileDescriptor =
    377             getContentResolver().openFileDescriptor(uri, "r");
    378     FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    379     Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    380     parcelFileDescriptor.close();
    381     return image;
    382 }
    383 </pre>
    384 
    385 <p> UI {@link android.os.AsyncTask} 
    386 {@link android.widget.ImageView} 
    387 
    388 </p>
    389 
    390 <h4>InputStream </h4>
    391 
    392 <p>URI  {@link java.io.InputStream} 
    393 </p>
    394 
    395 <pre>private String readTextFromUri(Uri uri) throws IOException {
    396     InputStream inputStream = getContentResolver().openInputStream(uri);
    397     BufferedReader reader = new BufferedReader(new InputStreamReader(
    398             inputStream));
    399     StringBuilder stringBuilder = new StringBuilder();
    400     String line;
    401     while ((line = reader.readLine()) != null) {
    402         stringBuilder.append(line);
    403     }
    404     fileInputStream.close();
    405     parcelFileDescriptor.close();
    406     return stringBuilder.toString();
    407 }
    408 </pre>
    409 
    410 <h3 id="create"></h3>
    411 
    412 <p>{@link android.content.Intent#ACTION_CREATE_DOCUMENT}  
    413 
    414  MIME 
    415 </p>
    416 
    417 
    418 <pre>
    419 // Here are some examples of how you might call this method.
    420 // The first parameter is the MIME type, and the second parameter is the name
    421 // of the file you are creating:
    422 //
    423 // createFile("text/plain", "foobar.txt");
    424 // createFile("image/png", "mypicture.png");
    425 
    426 // Unique request code.
    427 private static final int WRITE_REQUEST_CODE = 43;
    428 ...
    429 private void createFile(String mimeType, String fileName) {
    430     Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    431 
    432     // Filter to only show results that can be &quot;opened&quot;, such as
    433     // a file (as opposed to a list of contacts or timezones).
    434     intent.addCategory(Intent.CATEGORY_OPENABLE);
    435 
    436     // Create a file with the requested MIME type.
    437     intent.setType(mimeType);
    438     intent.putExtra(Intent.EXTRA_TITLE, fileName);
    439     startActivityForResult(intent, WRITE_REQUEST_CODE);
    440 }
    441 </pre>
    442 
    443 <p>{@link android.app.Activity#onActivityResult onActivityResult()}  URI 
    444 
    445 </p>
    446 
    447 <h3 id="delete"></h3>
    448 
    449 <p> URI {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE}  {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} 
    450 
    451 
    452 
    453 </p>
    454 
    455 <pre>
    456 DocumentsContract.deleteDocument(getContentResolver(), uri);
    457 </pre>
    458 
    459 <h3 id="edit"></h3>
    460 
    461 <p>SAF   {@link android.content.Intent#ACTION_OPEN_DOCUMENT}  {@link android.content.Intent#CATEGORY_OPENABLE} 
    462 
    463 
    464 
    465  </p>
    466 
    467 <pre>
    468 private static final int EDIT_REQUEST_CODE = 44;
    469 /**
    470  * Open a file for writing and append some text to it.
    471  */
    472  private void editDocument() {
    473     // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    474     // file browser.
    475     Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    476 
    477     // Filter to only show results that can be &quot;opened&quot;, such as a
    478     // file (as opposed to a list of contacts or timezones).
    479     intent.addCategory(Intent.CATEGORY_OPENABLE);
    480 
    481     // Filter to show only text files.
    482     intent.setType(&quot;text/plain&quot;);
    483 
    484     startActivityForResult(intent, EDIT_REQUEST_CODE);
    485 }
    486 </pre>
    487 
    488 <p>{@link android.app.Activity#onActivityResult onActivityResult()}<a href="#results"></a> {@link android.content.ContentResolver}  {@link java.io.FileOutputStream} 
    489 
    490 
    491 
    492 
    493 </p>
    494 
    495 <pre>private void alterDocument(Uri uri) {
    496     try {
    497         ParcelFileDescriptor pfd = getActivity().getContentResolver().
    498                 openFileDescriptor(uri, "w");
    499         FileOutputStream fileOutputStream =
    500                 new FileOutputStream(pfd.getFileDescriptor());
    501         fileOutputStream.write(("Overwritten by MyCloud at " +
    502                 System.currentTimeMillis() + "\n").getBytes());
    503         // Let the document provider know you're done by closing the stream.
    504         fileOutputStream.close();
    505         pfd.close();
    506     } catch (FileNotFoundException e) {
    507         e.printStackTrace();
    508     } catch (IOException e) {
    509         e.printStackTrace();
    510     }
    511 }</pre>
    512 
    513 <h3 id="permissions"></h3>
    514 
    515 <p> URI 
    516  5  
    517 
    518 
    519 
    520 </p>
    521 
    522 <p> URI 
    523 
    524 
    525 </p>
    526 
    527 
    528 <pre>final int takeFlags = intent.getFlags()
    529             &amp; (Intent.FLAG_GRANT_READ_URI_PERMISSION
    530             | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    531 // Check for the freshest data.
    532 getContentResolver().takePersistableUriPermission(uri, takeFlags);</pre>
    533 
    534 <p> 1  URI &mdash;
    535 
    536  {@code getContentResolver().takePersistableUriPermission()} 
    537 
    538 </p>
    539 
    540 <h2 id="custom">  </h2>
    541 
    542 <p>
    543    SAF 
    544 
    545 
    546 </p>
    547 
    548 
    549 <h3 id="manifest"></h3>
    550 
    551 <p>  
    552 </p>
    553 <ul>
    554 
    555 <li>API  19 </li>
    556 
    557 <li>   <code>&lt;provider&gt;</code> 
    558  </li>
    559 
    560 <li>: <code>com.example.android.storageprovider.MyCloudProvider</code>
    561 </li>
    562 
    563 <li> <code>com.example.android.storageprovider</code> <code>documents</code>
    564 
    565 {@code com.example.android.storageprovider.documents} </li>
    566 
    567 <li><code>&quot;true&quot;</code>  <code>android:exported</code>
    568 </li>
    569 
    570 <li><code>&quot;true&quot;</code>  <code>android:grantUriPermissions</code>
    571 
    572 <a href="#permissions"></a>
    573 </li>
    574 
    575 <li>{@code MANAGE_DOCUMENTS} 
    576 
    577 </li>
    578 
    579 <li>  {@code android:enabled} 
    580 Android 4.3 {@code android:enabled="@bool/atLeastKitKat"} 
    581 
    582 
    583 <ul>
    584 <li>{@code res/values/}  {@code bool.xml}  
    585  <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;false&lt;/bool&gt;</pre></li>
    586 
    587 <li>{@code res/values-v19/}  {@code bool.xml}  
    588  <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;true&lt;/bool&gt;</pre></li>
    589 </ul></li>
    590 
    591 <li>{@code android.content.action.DOCUMENTS_PROVIDER}  
    592 
    593 </li>
    594 
    595 </ul>
    596 <p> </p>
    597 
    598 <pre>&lt;manifest... &gt;
    599     ...
    600     &lt;uses-sdk
    601         android:minSdkVersion=&quot;19&quot;
    602         android:targetSdkVersion=&quot;19&quot; /&gt;
    603         ....
    604         &lt;provider
    605             android:name=&quot;com.example.android.storageprovider.MyCloudProvider&quot;
    606             android:authorities=&quot;com.example.android.storageprovider.documents&quot;
    607             android:grantUriPermissions=&quot;true&quot;
    608             android:exported=&quot;true&quot;
    609             android:permission=&quot;android.permission.MANAGE_DOCUMENTS&quot;
    610             android:enabled=&quot;&#64;bool/atLeastKitKat&quot;&gt;
    611             &lt;intent-filter&gt;
    612                 &lt;action android:name=&quot;android.content.action.DOCUMENTS_PROVIDER&quot; /&gt;
    613             &lt;/intent-filter&gt;
    614         &lt;/provider&gt;
    615     &lt;/application&gt;
    616 
    617 &lt;/manifest&gt;</pre>
    618 
    619 <h4 id="43">Android 4.3 </h4>
    620 
    621 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT} Android 4.4 Android 4.3  {@link android.content.Intent#ACTION_GET_CONTENT} Android 4.4  {@link android.content.Intent#ACTION_GET_CONTENT}  
    622 
    623 
    624 
    625 
    626 
    627   {@link android.content.Intent#ACTION_GET_CONTENT} 
    628 
    629   UI  2  2 
    630 
    631 </p>
    632 
    633 <p>Android  4.4  {@link android.content.Intent#ACTION_GET_CONTENT}  
    634 
    635 </p>
    636 
    637 <ol>
    638 <li>{@code res/values/}  {@code bool.xml}  
    639  <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;true&lt;/bool&gt;</pre></li>
    640 
    641 <li>{@code res/values-v19/}  {@code bool.xml}  
    642  <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;false&lt;/bool&gt;</pre></li>
    643 
    644 <li>4.4 API  19 {@link android.content.Intent#ACTION_GET_CONTENT}  <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"> </a>
    645 
    646 
    647 
    648 
    649 <pre>
    650 &lt;!-- This activity alias is added so that GET_CONTENT intent-filter
    651      can be disabled for builds on API level 19 and higher. --&gt;
    652 &lt;activity-alias android:name=&quot;com.android.example.app.MyPicker&quot;
    653         android:targetActivity=&quot;com.android.example.app.MyActivity&quot;
    654         ...
    655         android:enabled=&quot;@bool/atMostJellyBeanMR2&quot;&gt;
    656     &lt;intent-filter&gt;
    657         &lt;action android:name=&quot;android.intent.action.GET_CONTENT&quot; /&gt;
    658         &lt;category android:name=&quot;android.intent.category.OPENABLE&quot; /&gt;
    659         &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; /&gt;
    660         &lt;data android:mimeType=&quot;image/*&quot; /&gt;
    661         &lt;data android:mimeType=&quot;video/*&quot; /&gt;
    662     &lt;/intent-filter&gt;
    663 &lt;/activity-alias&gt;
    664 </pre>
    665 </li>
    666 </ol>
    667 <h3 id="contract"></h3>
    668 
    669 <p>  <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContractClass"> </a>  
    670 
    671 
    672   {@code public final} URIMIME 
    673 
    674   SAF 
    675 
    676 </p>
    677 
    678 <ul>
    679    <li>{@link android.provider.DocumentsContract.Document}</li>
    680    <li>{@link android.provider.DocumentsContract.Root}</li>
    681 </ul>
    682 
    683 <p> 
    684 </p>
    685 
    686 <pre>private static final String[] DEFAULT_ROOT_PROJECTION =
    687         new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
    688         Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
    689         Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
    690         Root.COLUMN_AVAILABLE_BYTES,};
    691 private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
    692         String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
    693         Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
    694         Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
    695 </pre>
    696 
    697 <h3 id="subclass"> DocumentsProvider</h3>
    698 
    699 <p>   {@link android.provider.DocumentsProvider} 
    700 
    701 </p>
    702 
    703 <ul>
    704 <li>{@link android.provider.DocumentsProvider#queryRoots queryRoots()}</li>
    705 
    706 <li>{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}</li>
    707 
    708 <li>{@link android.provider.DocumentsProvider#queryDocument queryDocument()}</li>
    709 
    710 <li>{@link android.provider.DocumentsProvider#openDocument openDocument()}</li>
    711 </ul>
    712 
    713 <p>
    714 {@link android.provider.DocumentsProvider} 
    715 </p>
    716 
    717 <h4 id="queryRoots">queryRoots </h4>
    718 
    719 <p>{@link android.provider.DocumentsProvider#queryRoots
    720 queryRoots()} {@link android.provider.DocumentsContract.Root}    {@link android.database.Cursor} 
    721 
    722 </p>
    723 
    724 <p> {@code projection} 
    725  1 &mdash;[]  [] 1  1 
    726 
    727  1  1 
    728  2 
    729 </p>
    730 
    731 <pre>
    732 &#64;Override
    733 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    734 
    735     // Create a cursor with either the requested fields, or the default
    736     // projection if "projection" is null.
    737     final MatrixCursor result =
    738             new MatrixCursor(resolveRootProjection(projection));
    739 
    740     // If user is not logged in, return an empty root cursor.  This removes our
    741     // provider from the list entirely.
    742     if (!isUserLoggedIn()) {
    743         return result;
    744     }
    745 
    746     // It's possible to have multiple roots (e.g. for multiple accounts in the
    747     // same app) -- just add multiple cursor rows.
    748     // Construct one row for a root called &quot;MyCloud&quot;.
    749     final MatrixCursor.RowBuilder row = result.newRow();
    750     row.add(Root.COLUMN_ROOT_ID, ROOT);
    751     row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
    752 
    753     // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
    754     // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
    755     // recently used documents will show up in the &quot;Recents&quot; category.
    756     // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
    757     // shares.
    758     row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
    759             Root.FLAG_SUPPORTS_RECENTS |
    760             Root.FLAG_SUPPORTS_SEARCH);
    761 
    762     // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
    763     row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
    764 
    765     // This document id cannot change once it's shared.
    766     row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
    767 
    768     // The child MIME types are used to filter the roots and only present to the
    769     //  user roots that contain the desired type somewhere in their file hierarchy.
    770     row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
    771     row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
    772     row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
    773 
    774     return result;
    775 }</pre>
    776 
    777 <h4 id="queryChildDocuments">queryChildDocuments </h4>
    778 
    779 <p>{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} {@link android.provider.DocumentsContract.Document}  {@link android.database.Cursor} 
    780 
    781 
    782 
    783 </p>
    784 
    785 <p> UI  
    786 
    787 &mdash;
    788 
    789 
    790 </p>
    791 
    792 <pre>&#64;Override
    793 public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    794                               String sortOrder) throws FileNotFoundException {
    795 
    796     final MatrixCursor result = new
    797             MatrixCursor(resolveDocumentProjection(projection));
    798     final File parent = getFileForDocId(parentDocumentId);
    799     for (File file : parent.listFiles()) {
    800         // Adds the file's display name, MIME type, size, and so on.
    801         includeFile(result, null, file);
    802     }
    803     return result;
    804 }
    805 </pre>
    806 
    807 <h4 id="queryDocument">queryDocument </h4>
    808 
    809 <p>{@link android.provider.DocumentsProvider#queryDocument queryDocument()} {@link android.provider.DocumentsContract.Document}  {@link android.database.Cursor} 
    810 
    811 
    812 
    813 </p>
    814 
    815 <p>{@link android.provider.DocumentsProvider#queryDocument queryDocument()} {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 
    816 
    817 
    818 </p>
    819 
    820 
    821 <pre>&#64;Override
    822 public Cursor queryDocument(String documentId, String[] projection) throws
    823         FileNotFoundException {
    824 
    825     // Create a cursor with the requested projection, or the default projection.
    826     final MatrixCursor result = new
    827             MatrixCursor(resolveDocumentProjection(projection));
    828     includeFile(result, documentId, null);
    829     return result;
    830 }
    831 </pre>
    832 
    833 <h4 id="openDocument">openDocument </h4>
    834 
    835 <p> {@link android.os.ParcelFileDescriptor} {@link android.provider.DocumentsProvider#openDocument
    836 openDocument()} 
    837  {@link android.os.ParcelFileDescriptor} 
    838   {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()} 
    839 
    840 
    841 </p>
    842 
    843 <pre>&#64;Override
    844 public ParcelFileDescriptor openDocument(final String documentId,
    845                                          final String mode,
    846                                          CancellationSignal signal) throws
    847         FileNotFoundException {
    848     Log.v(TAG, &quot;openDocument, mode: &quot; + mode);
    849     // It's OK to do network operations in this method to download the document,
    850     // as long as you periodically check the CancellationSignal. If you have an
    851     // extremely large file to transfer from the network, a better solution may
    852     // be pipes or sockets (see ParcelFileDescriptor for helper methods).
    853 
    854     final File file = getFileForDocId(documentId);
    855 
    856     final boolean isWrite = (mode.indexOf('w') != -1);
    857     if(isWrite) {
    858         // Attach a close listener if the document is opened in write mode.
    859         try {
    860             Handler handler = new Handler(getContext().getMainLooper());
    861             return ParcelFileDescriptor.open(file, accessMode, handler,
    862                         new ParcelFileDescriptor.OnCloseListener() {
    863                 &#64;Override
    864                 public void onClose(IOException e) {
    865 
    866                     // Update the file with the cloud server. The client is done
    867                     // writing.
    868                     Log.i(TAG, &quot;A file with id &quot; +
    869                     documentId + &quot; has been closed!
    870                     Time to &quot; +
    871                     &quot;update the server.&quot;);
    872                 }
    873 
    874             });
    875         } catch (IOException e) {
    876             throw new FileNotFoundException(&quot;Failed to open document with id &quot;
    877             + documentId + &quot; and mode &quot; + mode);
    878         }
    879     } else {
    880         return ParcelFileDescriptor.open(file, accessMode);
    881     }
    882 }
    883 </pre>
    884 
    885 <h3 id="security"></h3>
    886 
    887 <p>   
    888 
    889 {@link android.provider.DocumentsProvider#queryRoots
    890 queryRoots()} 
    891 </p>
    892 
    893 <pre>
    894 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    895 ...
    896     // If user is not logged in, return an empty root cursor.  This removes our
    897     // provider from the list entirely.
    898     if (!isUserLoggedIn()) {
    899         return result;
    900 }
    901 </pre>
    902 
    903 <p> {@code getContentResolver().notifyChange()} {@link android.provider.DocumentsContract} 
    904  URI   
    905 
    906 {@link android.provider.DocumentsProvider#queryRoots queryRoots()} 
    907 
    908 
    909 </p>
    910 
    911 <pre>private void onLoginButtonClick() {
    912     loginOrLogout();
    913     getContentResolver().notifyChange(DocumentsContract
    914             .buildRootsUri(AUTHORITY), null);
    915 }
    916 </pre>