Home | History | Annotate | Download | only in providers
      1 page.title=Khun kh Truy cp Kho lu tr
      2 @jd:body
      3 <div id="qv-wrapper">
      4 <div id="qv">
      5 
      6 <h2>Trong ti liu ny
      7  <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle">
      8         <span class="more">hin nhiu hn</span>
      9         <span class="less" style="display:none">hin t hn</span></a></h2>
     10 <ol id="toc44" class="hide-nested">
     11     <li>
     12         <a href="#overview">Tng quan</a>
     13     </li>
     14     <li>
     15         <a href="#flow">Dng iu khin</a>
     16     </li>
     17     <li>
     18         <a href="#client">Ghi mt ng dng My khch</a>
     19         <ol>
     20         <li><a href="#search">Tm kim ti liu</a></li>
     21         <li><a href="#process">Kt qu tin trnh</a></li>
     22         <li><a href="#metadata">Kim tra siu d liu ti liu</a></li>
     23         <li><a href="#open">M mt ti liu</a></li>
     24         <li><a href="#create">To mt ti liu mi</a></li>
     25         <li><a href="#delete">Xa mt ti liu</a></li>
     26         <li><a href="#edit">Chnh sa mt ti liu</a></li>
     27         <li><a href="#permissions">C nh cc quyn</a></li>
     28         </ol>
     29     </li>
     30     <li><a href="#custom">Ghi mt Trnh cung cp Ti liu Ty chnh</a>
     31         <ol>
     32         <li><a href="#manifest">Bn k khai</a></li>
     33         <li><a href="#contract">Hp ng</a></li>
     34         <li><a href="#subclass">Phn lp con DocumentsProvider</a></li>
     35         <li><a href="#security">Bo mt</a></li>
     36         </ol>
     37     </li>
     38 
     39 </ol>
     40 <h2>Lp kha</h2>
     41 <ol>
     42     <li>{@link android.provider.DocumentsProvider}</li>
     43     <li>{@link android.provider.DocumentsContract}</li>
     44 </ol>
     45 
     46 <h2>Video</h2>
     47 
     48 <ol>
     49     <li><a href="http://www.youtube.com/watch?v=zxHVeXbK1P4">
     50 DevBytes: Khun kh Truy cp Kho lu tr Android 4.4: Trnh cung cp</a></li>
     51      <li><a href="http://www.youtube.com/watch?v=UFj9AEz0DHQ">
     52 DevBytes: Khun kh Truy cp Kho lu tr Android 4.4: My khch</a></li>
     53 </ol>
     54 
     55 
     56 <h2>M V d</h2>
     57 
     58 <ol>
     59     <li><a href="{@docRoot}samples/StorageProvider/index.html">
     60 Trnh cung cp Lu tr</a></li>
     61      <li><a href="{@docRoot}samples/StorageClient/index.html">
     62 StorageClient</a></li>
     63 </ol>
     64 
     65 <h2>Xem thm</h2>
     66 <ol>
     67     <li>
     68         <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
     69         Ni dung C bn v Trnh cung cp Ni dung
     70         </a>
     71     </li>
     72 </ol>
     73 
     74 </div>
     75 </div>
     76 
     77 
     78 <p>Android 4.4 (API mc 19) gii thiu Khun kh Truy cp Kho lu tr (SAF). SAF
     79  gip ngi dng n gin ha vic duyt v m ti liu, hnh nh v cc tp khc
     80 gia tt c trnh cung cp lu tr ti liu m h thch. UI tiu chun, d s dng
     81 cho php ngi dng duyt tp v truy cp hot ng gn y mt cch nht qun gia cc ng dng v trnh cung cp.</p>
     82 
     83 <p>Dch v lu tr m my hoc cc b c th tham gia vo h sinh thi ny bng cch trin khai mt
     84 {@link android.provider.DocumentsProvider}  gi gn cc dch v ca mnh. Nhng ng dng
     85 my khch cn truy cp vo ti liu ca mt trnh cung cp c th tch hp vi SAF ch bng mt vi
     86 dng m.</p>
     87 
     88 <p>SAF bao gm:</p>
     89 
     90 <ul>
     91 <li><strong>Trnh cung cp ti liu</strong>&mdash;Mt trnh cung cp ni dung cho php mt
     92 dch v lu tr (chng hn nh Google Drive) pht hin cc tp m n qun l. Trnh cung cp ti liu c
     93 trin khai thnh mt lp con ca lp {@link android.provider.DocumentsProvider}.
     94 S  ti liu-trnh cung cp s c da trn mt phn cp tp truyn thng,
     95 cho d cch thc trnh cung cp ti liu ca bn trc tip lu tr d liu l hon ton do bn.
     96 Nn tng Android bao gm mt vi trnh cung cp ti liu tch hp, chng hn nh
     97 Downloads, Images, v Videos.</li>
     98 
     99 <li><strong>ng dng my khch</strong>&mdash;Mt ng dng ty chnh c chc nng gi ra  nh
    100 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} v/hoc
    101 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} v nhn cc tp
    102 c tr v bi trnh cung cp ti liu.</li>
    103 
    104 <li><strong>B chn</strong>&mdash;Mt UI h thng cho php ngi dng truy cp ti liu t tt c
    105 trnh cung cp ti liu m tha mn cc tiu ch tm kim ca ng dng my khch.</li>
    106 </ul>
    107 
    108 <p>Mt s tnh nng c SAF cung cp bao gm:</p>
    109 <ul>
    110 <li>Cho php ngi dng duyt ni dung t tt c trnh cung cp ti liu, khng ch mt ng dng duy nht.</li>
    111 <li>Gip ng dng ca bn c th c quyn truy cp lu di, c nh vo
    112  cc ti liu c s hu bi mt trnh cung cp ti liu. Thng qua truy cp ny, ngi dng c th thm, chnh sa,
    113  lu v xa tp trn trnh cung cp.</li>
    114 <li>H tr nhiu ti khon ngi dng v cc phn gc tm thi chng hn nh trnh cung cp
    115 b nh USB, n ch xut hin nu  a c cm vo. </li>
    116 </ul>
    117 
    118 <h2 id ="overview">Tng quan</h2>
    119 
    120 <p>SAF tp trung xoay quanh mt trnh cung cp ni dung l mt lp con
    121 ca lp {@link android.provider.DocumentsProvider}. Trong mt <em>trnh cung cp ti liu</em>, d liu c
    122 cu trc thnh mt phn cp tp truyn thng:</p>
    123 <p><img src="{@docRoot}images/providers/storage_datamodel.png" alt="data model" /></p>
    124 <p class="img-caption"><strong>Hnh 1.</strong> M hnh d liu ca trnh cung cp ti liu. Mt Phn gc ch n mt Ti liu duy nht,
    125 sau  n bt u xe ra ton b cy.</p>
    126 
    127 <p>Lu  iu sau y:</p>
    128 <ul>
    129 
    130 <li>Mi mt trnh cung cp ti liu s bo co mt hoc nhiu
    131 "phn gc" l im bt u khm ph cy ti liu.
    132 Mi phn gc c mt {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID} duy nht,
    133 v n tr n mt ti liu (th mc)
    134 biu din ni dung bn di phn gc .
    135 Phn gc c th linh hot theo thit k  h tr cc trng hp s dng nh nhiu ti khon,
    136 thit b lu tr USB tm thi, hoc ng nhp/ng xut ngi dng.</li>
    137 
    138 <li>Di mi phn gc l mt ti liu n l. Ti liu  s tr ti 1 n <em>N</em> ti liu,
    139 mi ti liu li c th tr ti 1 n <em>N</em> ti liu khc. </li>
    140 
    141 <li>Mi b nh ph tr ph b mt
    142 cc tp v th mc ring l bng cch tham chiu chng bng mt
    143 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} duy nht.
    144 ID ca ti liu phi l duy nht v khng thay i sau khi c pht hnh, do chng c s dng  cp URI
    145 khng thay i gia cc ln khi ng li thit b.</li>
    146 
    147 
    148 <li>Ti liu c th l mt tp m c (c mt kiu MIME c th), hoc mt
    149 th mc cha cc ti liu b sung (c kiu MIME 
    150 {@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR}).</li>
    151 
    152 <li>Mi ti liu c th c cc kh nng khc nhau nh c m t bi
    153 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}.
    154 V d, {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE},
    155 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE}, v
    156 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}.
    157 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} cng c th
    158 c trong nhiu th mc.</li>
    159 </ul>
    160 
    161 <h2 id="flow">Dng iu khin</h2>
    162 <p>Nh nu trn, m hnh d liu ca trnh cung cp ti liu c da trn mt phn cp
    163 tp truyn thng. Tuy nhin, bn c th thc t lu tr d liu ca mnh bng bt k cch no m mnh thch, min
    164 l n c th c truy cp thng qua API {@link android.provider.DocumentsProvider}. V d, bn
    165 c th s dng kho lu tr m my da trn tag cho d liu ca mnh.</p>
    166 
    167 <p>Hnh 2 minh ha mt v d v cch m mt ng dng nh c th s dng SAF
    168  truy cp d liu c lu tr:</p>
    169 <p><img src="{@docRoot}images/providers/storage_dataflow.png" alt="app" /></p>
    170 
    171 <p class="img-caption"><strong>Hnh 2.</strong> Dng Khun kh Truy cp Kho lu tr</p>
    172 
    173 <p>Lu  iu sau y:</p>
    174 <ul>
    175 
    176 <li>Trong SAF, trnh cung cp v my khch khng tng tc
    177 trc tip vi nhau. Mt my khch yu cu quyn  tng tc
    178 vi tp (c th l quyn c, chnh sa, to hoc xa tp).</li>
    179 
    180 <li>Tng tc bt u khi mt ng dng (trong v d ny ny mt ng dng nh) th hin  nh 
    181 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} hoc {@link android.content.Intent#ACTION_CREATE_DOCUMENT}.  nh c th bao gm cc b lc
    182  c th hn cc tiu ch&mdash;v d, "cp cho ti tt c tp m c
    183 c kiu MIME l 'image'."</li>
    184 
    185 <li>Sau khi  nh th hin, b chn ca h thng s i n tng trnh cung cp c ng k
    186 v hin th cho ngi dng xem cc phn gc ni dung khp vi tiu ch.</li>
    187 
    188 <li>B chn cp cho ngi dng mt giao din tiu chun  truy cp ti liu, mc
    189 d cc trnh cung cp ti liu lin quan c th rt khc nhau. V d, hnh 2
    190 minh ha mt trnh cung cp Google Drive, mt trnh cung cp USB, v mt trnh cung cp m my.</li>
    191 </ul>
    192 
    193 <p>Hnh 3 minh ha mt b chn m trong  mt ngi dng ang tm kim hnh nh  chn mt
    194 ti khon Google Drive:</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>Hnh 3.</strong> B chn</p>
    199 
    200 <p>Khi ngi dng chn Google Drive, hnh nh c hin th nh minh ha trong
    201 hnh 4. T im  tr i, ngi dng c th tng tc vi chng theo bt k cch no
    202 c h tr bi trnh cung cp v ng dng my khch.
    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>Hnh 4.</strong> Hnh nh</p>
    207 
    208 <h2 id="client">Ghi mt ng dng My khch</h2>
    209 
    210 <p>Trn phin bn Android 4.3 v thp hn, nu bn mun ng dng ca mnh truy xut mt tp t mt ng dng
    211 khc, n phi gi ra mt  nh chng hn nh {@link android.content.Intent#ACTION_PICK}
    212 hay {@link android.content.Intent#ACTION_GET_CONTENT}. Khi , ngi dng phi chn
    213 mt ng dng duy nht m t  h chn mt tp v ng dng c chn phi cung cp mt
    214 giao din ngi dng  ngi dng duyt v chn t cc tp c sn. </p>
    215 
    216 <p>Trn phin bn Android 4.4 tr ln, bn c thm mt ty chn l s dng  nh
    217 {@link android.content.Intent#ACTION_OPEN_DOCUMENT},
    218 n hin th mt UI b chn c iu khin bi h thng, cho php ngi dng
    219 duyt tt c tp m cc ng dng khc  cung cp. T UI duy nht ny, ngi dng
    220 c th chn mt tp t bt k ng dng no c h tr.</p>
    221 
    222 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT} khng
    223 nhm mc ch thay th cho {@link android.content.Intent#ACTION_GET_CONTENT}.
    224 Bn nn s dng ci no s ph thuc vo nhu cu ca ng dng ca bn:</p>
    225 
    226 <ul>
    227 <li>S dng {@link android.content.Intent#ACTION_GET_CONTENT} nu bn mun ng dng ca mnh
    228 ch n thun c/nhp d liu. Bng cch ny, ng dng nhp mt bn sao d liu,
    229 chng hn nh mt tp hnh nh.</li>
    230 
    231 <li>S dng {@link android.content.Intent#ACTION_OPEN_DOCUMENT} nu bn mun ng dng
    232 ca mnh c quyn truy cp lu di, c nh vo cc ti liu c s hu bi mt
    233 trnh cung cp ti liu. V d nh trng hp mt ng dng chnh sa nh cho php ngi dng chnh sa
    234 cc hnh nh c lu tr trong mt trnh cung cp ti liu. </li>
    235 
    236 </ul>
    237 
    238 
    239 <p>Phn ny m t cch ghi cc ng dng my khch da trn
    240 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} v
    241 cc  nh {@link android.content.Intent#ACTION_CREATE_DOCUMENT}.</p>
    242 
    243 
    244 <h3 id="search">Tm kim ti liu</h3>
    245 
    246 <p>
    247 on m HTML sau s dng {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    248  tm kim cc trnh cung cp ti liu m
    249 cha tp hnh nh:</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>Lu  iu sau y:</p>
    276 <ul>
    277 <li>Khi ng dng th hin  nh {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    278 , n s khi chy mt b chn  hin th tt c trnh cung cp ti liu khp vi tiu ch.</li>
    279 
    280 <li>Thm th loi {@link android.content.Intent#CATEGORY_OPENABLE} vo
    281  nh s lc kt qu  ch hin th nhng ti liu c th m c, chng hn nh tp hnh nh.</li>
    282 
    283 <li>Cu lnh {@code intent.setType("image/*")} s lc thm 
    284 ch hin th nhng ti liu c kiu d liu MIME hnh nh.</li>
    285 </ul>
    286 
    287 <h3 id="results">Kt qu Tin trnh</h3>
    288 
    289 <p>Sau khi ngi dng chn mt ti liu trong b chn,
    290 {@link android.app.Activity#onActivityResult onActivityResult()} s c gi.
    291 URI tro ti ti liu c chn s nm trong tham s {@code resultData}
    292 . Trch xut UI bng cch s dng {@link android.content.Intent#getData getData()}.
    293 Sau khi c n, bn c th s dng n  truy xut ti liu m ngi dng mun. V
    294 d:</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">Kim tra siu d liu ti liu</h3>
    320 
    321 <p>Sau khi c URI cho mt ti liu, bn c quyn truy cp siu d liu ca n. on m HTML
    322 ny bt siu d liu cho mt ti liu c quy nh bi URI, v ghi li n:</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">M mt ti liu</h3>
    367 
    368 <p>Sau khi c URI cho mt ti liu, bn c th m n hoc lm bt k iu g
    369 m bn mun.</p>
    370 
    371 <h4>Bitmap</h4>
    372 
    373 <p>Sau y l mt v d v cch bn c th m mt {@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>Lu  rng bn khng nn thc hin thao tc ny trn lung UI. Thc hin iu ny di
    386 nn bng cch s dng {@link android.os.AsyncTask}. Sau khi m bitmap, bn c th
    387 hin th n trong mt {@link android.widget.ImageView}.
    388 </p>
    389 
    390 <h4>Nhn mt InputStream</h4>
    391 
    392 <p>Sau y l mt v d v cch m bn c th nhn mt {@link java.io.InputStream} t URI. Trong on m HTML
    393 ny, cc dng tp ang c c thnh mt xu:</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">To mt ti liu mi</h3>
    411 
    412 <p>ng dng ca bn c th to mt ti liu mi trong mt trnh cung cp ti liu bng cch s dng  nh
    413 {@link android.content.Intent#ACTION_CREATE_DOCUMENT}
    414 .  to mt tp, bn cp cho  nh ca mnh mt kiu MIME v tn tp, v
    415 khi chy n bng mt m yu cu duy nht. Phn cn li s c lm h bn:</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>Sau khi to mt ti liu mi, bn c th nhn URI ca ti liu trong
    444 {@link android.app.Activity#onActivityResult onActivityResult()}, sao cho bn
    445 c th tip tc ghi n.</p>
    446 
    447 <h3 id="delete">Xa mt ti liu</h3>
    448 
    449 <p>Nu bn c URI cho mt ti liu v
    450 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS}
    451 ca ti liu cha
    452 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},
    453 bn c th xa ti liu . V d:</p>
    454 
    455 <pre>
    456 DocumentsContract.deleteDocument(getContentResolver(), uri);
    457 </pre>
    458 
    459 <h3 id="edit">Chnh sa mt ti liu</h3>
    460 
    461 <p>Bn c th s dng SAF  chnh sa mt ti liu vn bn ngay ti ch.
    462 on m HTML ny th hin
    463  nh {@link android.content.Intent#ACTION_OPEN_DOCUMENT} v s dng
    464 th loi {@link android.content.Intent#CATEGORY_OPENABLE}  ch hin th
    465 nhng ti liu c th m c. N lc thm  ch hin th nhng tp vn bn:</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>Tip theo, t {@link android.app.Activity#onActivityResult onActivityResult()}
    489 (xem <a href="#results">Kt qu tin trnh</a>) bn c th gi m  thc hin chnh sa.
    490 on m HTML sau nhn c mt {@link java.io.FileOutputStream}
    491 t {@link android.content.ContentResolver}. Theo mc nh, n s dng ch  ghi.
    492 Cch tt nht l yu cu lng quyn truy cp bn cn  mc t nht, v th ng yu cu
    493 quyn c/ghi nu bn ch cn quyn ghi:</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">C nh cc quyn</h3>
    514 
    515 <p>Khi ng dng ca bn m mt tp  c hoc ghi, h thng s cp cho
    516 ng dng ca bn mt quyn URI c cp cho tp . Quyn ny s ko di ti khi thit b ca bn khi ng li.
    517 Nhng gi s ng dng ca bn l mt ng dng chnh sa hnh nh, v bn mun ngi dng c th
    518 truy cp 5 hnh nh cui cng m h  chnh sa, trc tip t ng dng ca bn. Nu thit b ca ngi dng
    519  khi ng li, bn s phi gi ngi dng tr li b chn h thng  tm
    520 cc tp , y r rng khng phi l cch l tng.</p>
    521 
    522 <p> trnh iu ny xy ra, bn c th c nh cc quyn m h thng
    523 cp cho ng dng ca bn. ng dng ca bn s "nhn" cp quyn URI c th c nh
    524 m h thng cung cp mt cch hiu qu. iu ny cho php ngi dng c quyn lin tc truy cp cc tp 
    525 thng qua ng dng ca bn, ngay c khi thit b  b khi ng li:</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>Cn mt bc cui cng. Bn c th  lu cc
    535 URI gn y nht m ng dng ca bn  truy cp, nhng chng cn th khng cn hp l&mdash;mt ng dng khc
    536 c th  xa hoc sa i ti liu. V th, bn lun nn gi
    537 {@code getContentResolver().takePersistableUriPermission()}  kim tra
    538 d liu mi nht.</p>
    539 
    540 <h2 id="custom">Ghi mt Trnh cung cp Ti liu Ty chnh</h2>
    541 
    542 <p>
    543 Nu bn ang pht trin mt ng dng cung cp dch v lu tr cho tp (chng hn nh
    544 mt dch v lu tr m my), bn c th cung cp cc tp ca mnh thng qua
    545 SAF bng cch ghi mt trnh cung cp ti liu ty chnh.  Phn ny m t cch lm iu
    546 ny.</p>
    547 
    548 
    549 <h3 id="manifest">Bn k khai</h3>
    550 
    551 <p> trin khai mt trnh cung cp ti liu ty chnh, hy thm ni dung sau vo bn k khai
    552 ca ng dng ca bn:</p>
    553 <ul>
    554 
    555 <li>Mt mc tiu API mc 19 hoc cao hn.</li>
    556 
    557 <li>Mt phn t <code>&lt;provider&gt;</code> khai bo trnh cung cp lu tr
    558 ty chnh ca bn. </li>
    559 
    560 <li>Tn ca trnh cung cp ca bn, l tn lp ca n, bao gm tn gi.
    561 V d: <code>com.example.android.storageprovider.MyCloudProvider</code>.</li>
    562 
    563 <li>Tn thm quyn ca bn, tc l tn gi ca bn (trong v d ny l
    564 <code>com.example.android.storageprovider</code>) cng vi kiu ca trnh cung cp ni dung
    565 (<code>documents</code>). V d, {@code com.example.android.storageprovider.documents}.</li>
    566 
    567 <li>Thuc tnh <code>android:exported</code> c t thnh <code>&quot;true&quot;</code>.
    568 Bn phi xut trnh cung cp ca mnh  cc ng dng khc c th thy n.</li>
    569 
    570 <li>Thuc tnh <code>android:grantUriPermissions</code> c t thnh
    571 <code>&quot;true&quot;</code>. Thit t ny cho php h thng cp cho cc ng dng khc quyn truy cp
    572 vo ni dung trong trnh cung cp ca bn.  tho lun v cch c nh quyn c cp cho
    573 mt ti liu c th, hy xem phn<a href="#permissions">C nh cc quyn</a>.</li>
    574 
    575 <li>Quyn {@code MANAGE_DOCUMENTS}. Theo mc nh, mt trnh cung cp s c sn
    576 i vi mi ngi. Vic thm quyn ny s hn ch trnh cung cp ca bn vo h thng.
    577 Hn ch ny c  ngha quan trng i vi vn  bo mt.</li>
    578 
    579 <li>Thuc tnh {@code android:enabled} c t thnh mt gi tr boolean c nh ngha trong mt tp
    580 ti nguyn. Mc ch ca thuc tnh ny l  v hiu ha trnh cung cp trn cc thit b chy phin bn Android 4.3 hoc thp hn.
    581 V d, {@code android:enabled="@bool/atLeastKitKat"}. Bn
    582 cnh vic nu thuc tnh ny trong bn k khai, bn cn lm nh sau:
    583 <ul>
    584 <li>Trong tp ti nguyn {@code bool.xml} ca bn bn di {@code res/values/}, hy thm
    585 dng sau: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;false&lt;/bool&gt;</pre></li>
    586 
    587 <li>Trong tp ti nguyn {@code bool.xml} ca bn bn di {@code res/values-v19/}, hy thm
    588 dng sau: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;true&lt;/bool&gt;</pre></li>
    589 </ul></li>
    590 
    591 <li>Mt b lc  nh cha hnh ng
    592 {@code android.content.action.DOCUMENTS_PROVIDER}, sao cho trnh cung cp ca bn
    593 xut hin trong b chn khi h thng tm kim trnh cung cp.</li>
    594 
    595 </ul>
    596 <p>Sau y l cc on trch t mt bn k khai mu cha mt trnh cung cp:</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">H tr cc thit b chy phin bn Android 4.3 v thp hn</h4>
    620 
    621 <p> nh
    622 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} ch c sn
    623 trn cc thit b chy phin bn Android 4.4 tr ln.
    624 Nu bn mun ng dng ca mnh h tr {@link android.content.Intent#ACTION_GET_CONTENT}
    625  to iu kin cho cc thit b ang chy phin bn Android 4.3 v thp hn, bn nn
    626 v hiu ha b lc  nh {@link android.content.Intent#ACTION_GET_CONTENT} trong
    627 bn k khai ca bn cho cc thit b chy phin bn Android 4.4 tr ln. Mt
    628 trnh cung cp ti liu v {@link android.content.Intent#ACTION_GET_CONTENT} nn c xem xt
    629  loi tr ln nhau. Nu bn h tr c hai ng thi, ng dng ca bn s
    630 xut hin hai ln trong UI ca b chn h thng, a ra hai cch khc nhau  truy cp
    631 d liu  lu ca bn. iu ny c th khin ngi dng b nhm ln.</p>
    632 
    633 <p>Sau y l cch c khuyn co  v hiu ha b lc  nh
    634 {@link android.content.Intent#ACTION_GET_CONTENT} i vi cc thit b
    635 chy phin bn Android 4.4 hoc cao hn:</p>
    636 
    637 <ol>
    638 <li>Trong tp ti nguyn {@code bool.xml} ca bn bn di {@code res/values/}, hy thm
    639 dng sau: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;true&lt;/bool&gt;</pre></li>
    640 
    641 <li>Trong tp ti nguyn {@code bool.xml} ca bn bn di {@code res/values-v19/}, hy thm
    642 dng sau: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;false&lt;/bool&gt;</pre></li>
    643 
    644 <li>Thm mt
    645 <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html">b danh
    646 hot ng</a>  v hiu ha b lc  nh {@link android.content.Intent#ACTION_GET_CONTENT}
    647 i vi cc phin bn 4.4 (API mc 19) tr ln. V d:
    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">Hp ng</h3>
    668 
    669 <p>Thng khi bn ghi mt trnh cung cp ni dung ty chnh, mt trong nhng tc v  l
    670 trin khai cc lp hp ng nh c m t trong hng dn cho nh pht trin
    671 <a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContractClass">
    672 Trnh cung cp Ni dung</a>. Lp hp ng l mt lp {@code public final} m
    673 cha cc nh ngha hng s cho URI, tn ct, kiu MIME v
    674 siu d liu khc lin quan ti trnh cung cp. SAF
    675 cung cp nhng lp hp ng ny cho bn, v th bn khng cn t
    676 ghi:</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>V d, sau y l cc ct bn c th tr v trong mt con chy khi
    684 trnh cung cp ti liu ca bn c truy vn v ti liu hoc phn gc:</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">Phn lp con DocumentsProvider</h3>
    698 
    699 <p>Bc tip theo trong khi ghi mt trnh cung cp ti liu ty chnh  l phn lp con
    700 cho lp tm tt {@link android.provider.DocumentsProvider}. Ti thiu, bn cn trin khai
    701 cc phng php sau:</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>y l nhng phng php duy nht m bn c yu cu phi trin khai, nhng cn
    714 nhiu phng php na m bn c th mun trin khai. Xem {@link android.provider.DocumentsProvider}
    715  bit chi tit.</p>
    716 
    717 <h4 id="queryRoots">Trin khai queryRoots</h4>
    718 
    719 <p>Vic bn trin khai {@link android.provider.DocumentsProvider#queryRoots
    720 queryRoots()} phi tr v mt {@link android.database.Cursor} tr v tt c
    721 th mc gc trong trnh cung cp ti liu ca bn, bng cch s dng cc ct c nh ngha trong
    722 {@link android.provider.DocumentsContract.Root}.</p>
    723 
    724 <p>Trong on m HTML sau, tham s {@code projection} biu din cc trng c th
    725 m hm gi mun nhn v. on m HTML to mt con chy mi
    726 v thm mt hng vo n&mdash;mt th mc gc, mc cao nht, nh
    727 Downloads hoc Images.  Hu ht cc trnh cung cp ch c mt phn gc. Bn c th c nhiu hn mt,
    728 v d, trong trng hp nhiu ti khon ngi dng. Trong trng hp , ch cn thm mt
    729 hng th hai vo con chy.</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">Trin khai queryChildDocuments</h4>
    778 
    779 <p>Vic bn trin khai
    780 {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
    781 phi tr v mt {@link android.database.Cursor} m ch n tt c tp trong
    782 th mc c ch nh, bng cch s dng cc ct c nh ngha trong
    783 {@link android.provider.DocumentsContract.Document}.</p>
    784 
    785 <p>Phng php ny c gi khi bn chn mt th mc gc ng dng trong UI b chn.
    786 N nhn c ti liu con ca mt th mc nm di phn gc.  N c th c gi  bt k mc no trong phn cp tp
    787 , khng ch phn gc. on m HTML
    788 ny to mt con chy mi bng cc ct c yu cu, sau  thm thng tin v
    789 mi tp con trc tip trong th mc m vo con chy.
    790 Tp con c th l mt hnh nh, mt th mc khc&mdash;bt k tp no:</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">Trin khai queryDocument</h4>
    808 
    809 <p>Vic bn trin khai
    810 {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    811 phi tr v mt {@link android.database.Cursor} m ch n tp c ch nh,
    812 bng cch s dng cc ct c nh ngha trong {@link android.provider.DocumentsContract.Document}.
    813 </p>
    814 
    815 <p>Phng php {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    816 tr v cng thng tin  c chuyn trong
    817 {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()},
    818 nhng l i vi mt tp c th:</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">Trin khai openDocument</h4>
    834 
    835 <p>Bn phi trin khai {@link android.provider.DocumentsProvider#openDocument
    836 openDocument()}  tr v mt {@link android.os.ParcelFileDescriptor} biu din
    837 tp c ch nh. Cc ng dng khc c th s dng {@link android.os.ParcelFileDescriptor}
    838 c tr v  truyn pht d liu. H thng gi phng php ny sau khi ngi dng chn mt tp
    839 v ng dng my khch yu cu truy cp n bng cch gi
    840 {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}.
    841 V d:</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">Bo mt</h3>
    886 
    887 <p>Gi s trnh cung cp ti liu ca bn l mt dch v lu tr m my c bo v bng mt khu
    888 v bn mun m bo rng ngi dng c ng nhp trc khi bn bt u chia s tp ca h.
    889 ng dng ca bn nn lm g nu ngi dng khng ng nhp?  Gii php l tr v
    890 phn gc 0 trong trin khai {@link android.provider.DocumentsProvider#queryRoots
    891 queryRoots()} ca bn. C th l mt con chy gc trng:</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>Bc cn li l gi {@code getContentResolver().notifyChange()}.
    904 Bn cn nh {@link android.provider.DocumentsContract} ch?  Chng ta ang s dng n  to
    905 URI ny. on m HTML sau bo cho h thng truy vn cc phn gc trong
    906 trnh cung cp ti liu ca bn bt c khi no trng thi ng nhp ca ngi dng thay i. Nu ngi dng khng c
    907 ng nhp, lnh gi ti {@link android.provider.DocumentsProvider#queryRoots queryRoots()} s tr v mt
    908 con chy trng nh minh ha bn trn. iu ny m bo rng ti liu ca mt trnh cung cp ch
    909 c sn nu ngi dng ng nhp vo trnh cung cp .</p>
    910 
    911 <pre>private void onLoginButtonClick() {
    912     loginOrLogout();
    913     getContentResolver().notifyChange(DocumentsContract
    914             .buildRootsUri(AUTHORITY), null);
    915 }
    916 </pre>