Home | History | Annotate | Download | only in providers
      1 page.title=Estrutura de acesso ao armazenamento
      2 @jd:body
      3 <div id="qv-wrapper">
      4 <div id="qv">
      5 
      6 <h2>Neste documento
      7  <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle">
      8         <span class="more">mostrar mais</span>
      9         <span class="less" style="display:none">mostrar menos</span></a></h2>
     10 <ol id="toc44" class="hide-nested">
     11     <li>
     12         <a href="#overview">Viso geral</a>
     13     </li>
     14     <li>
     15         <a href="#flow">Controlar fluxo</a>
     16     </li>
     17     <li>
     18         <a href="#client">Programao de um aplicativo cliente</a>
     19         <ol>
     20         <li><a href="#search">Busca de documentos</a></li>
     21         <li><a href="#process">Processamento de resultados</a></li>
     22         <li><a href="#metadata">Examinao de metadados de documentos</a></li>
     23         <li><a href="#open">Abertura de um documento</a></li>
     24         <li><a href="#create">Criao de um novo documento</a></li>
     25         <li><a href="#delete">Excluso de um documento</a></li>
     26         <li><a href="#edit">Edio de um documento</a></li>
     27         <li><a href="#permissions">Manuteno de permisses</a></li>
     28         </ol>
     29     </li>
     30     <li><a href="#custom">Criao de um provedor de documentos personalizado</a>
     31         <ol>
     32         <li><a href="#manifest">Manifesto</a></li>
     33         <li><a href="#contract">Contratos</a></li>
     34         <li><a href="#subclass">Subclasse DocumentsProvider</a></li>
     35         <li><a href="#security">Segurana</a></li>
     36         </ol>
     37     </li>
     38 
     39 </ol>
     40 <h2>Classes principais</h2>
     41 <ol>
     42     <li>{@link android.provider.DocumentsProvider}</li>
     43     <li>{@link android.provider.DocumentsContract}</li>
     44 </ol>
     45 
     46 <h2>Vdeos</h2>
     47 
     48 <ol>
     49     <li><a href="http://www.youtube.com/watch?v=zxHVeXbK1P4">
     50 DevBytes: estrutura de acesso ao armazenamento do Android 4.4: Provedor</a></li>
     51      <li><a href="http://www.youtube.com/watch?v=UFj9AEz0DHQ">
     52 DevBytes: estrutura de acesso ao armazenamento do Android 4.4: Cliente</a></li>
     53 </ol>
     54 
     55 
     56 <h2>Amostras de cdigo</h2>
     57 
     58 <ol>
     59     <li><a href="{@docRoot}samples/StorageProvider/index.html">
     60 Provedor de armazenamento</a></li>
     61      <li><a href="{@docRoot}samples/StorageClient/index.html">
     62 StorageClient</a></li>
     63 </ol>
     64 
     65 <h2>Veja tambm</h2>
     66 <ol>
     67     <li>
     68         <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
     69         Preceitos do provedor de contedo
     70         </a>
     71     </li>
     72 </ol>
     73 
     74 </div>
     75 </div>
     76 
     77 
     78 <p>O Android 4.4 (API de nvel 19) introduz a Estrutura de Acesso ao Armazenamento (SAF). A SAF
     79  simplifica para os usurios a busca e abertura de documentos, imagens e outros arquivos
     80 dentre todos os provedores de armazenamento de documentos de preferncia. A interface grfica padro  fcil de usar
     81 e permite aos usurios buscar arquivos e acessar arquivos recentes de modo coerente em todos os aplicativos e provedores.</p>
     82 
     83 <p>Servios de armazenamento local ou em nuvem podem participar desse ecossistema por meio da implementao
     84 de um {@link android.provider.DocumentsProvider} que encapsula os servios. Aplicativos
     85 clientes que precisam acessar documentos de um provedor podem integrar-se com a SAF com apenas algumas
     86 linhas de cdigo.</p>
     87 
     88 <p>A SAF contm:</p>
     89 
     90 <ul>
     91 <li><strong>Provedor de documentos</strong> &mdash; Provedor de contedo que oferece
     92 um servio de armazenamento (como o Google Drive) para exibir os arquivos que gerencia. O provedor de documentos
     93  implementado como uma subclasse da classe {@link android.provider.DocumentsProvider}.
     94 O esquema do provedor de documento se baseia em uma hierarquia de arquivo tradicional,
     95 embora o modo de armazenamento fsico de dados do provedor de documentos seja definido pelo programador.
     96 A plataforma do Android contm diversos provedores de documento embutidos, como
     97 Downloads, Imagens e Vdeos.</li>
     98 
     99 <li><strong>Aplicativo cliente</strong> &mdash; Aplicativo personalizado que chama a inteno
    100 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    101 e/ou {@link android.content.Intent#ACTION_CREATE_DOCUMENT} e recebe
    102 os arquivos retornados pelos provedores de documentos.</li>
    103 
    104 <li><strong>Seletor</strong> &mdash; IU de sistema que permite aos usurios acessar documentos de todos
    105 os provedores de documentos que satisfazem os critrios de busca do aplicativo cliente.</li>
    106 </ul>
    107 
    108 <p>A seguir h alguns recursos oferecidos pela SAF:</p>
    109 <ul>
    110 <li>Permitir que usurios busquem contedo de todos os provedores de documentos, no somente de um nico aplicativo.</li>
    111 <li>Possibilitar ao aplicativo a obteno de acesso persistente e de longo prazo
    112 a documentos de propriedade de um provedor de documentos. Por meio deste acesso, os usurios podem adicionar, editar,
    113  salvar e excluir arquivos no provedor.</li>
    114 <li> compatvel com diversas contas de usurio e razes transitrias como provedores
    115 de armazenamento USB, que s aparecem se o dispositivo estiver plugado. </li>
    116 </ul>
    117 
    118 <h2 id ="overview">Viso geral</h2>
    119 
    120 <p>A SAF consiste em um provedor de contedo que  uma
    121 subclasse da classe {@link android.provider.DocumentsProvider}. Dentro de um <em>provedor de documentos</em>, os dados
    122 so estruturados como uma hierarquia de arquivo tradicional:</p>
    123 <p><img src="{@docRoot}images/providers/storage_datamodel.png" alt="data model" /></p>
    124 <p class="img-caption"><strong>Figura 1.</strong> Modelo de dados do provedor de documentos Uma Raiz aponta para um nico Documento,
    125 que ento inicia o fan-out de toda a rvore.</p>
    126 
    127 <p>Observe o seguinte:</p>
    128 <ul>
    129 
    130 <li>Cada provedor de documentos relata uma ou mais
    131 "razes", que so pontos de partida na explorao de uma rvore de documentos.
    132 Cada raiz tem um {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID} exclusivo
    133 e ele aponta para um documento (um diretrio)
    134 representando o contedo sob essa raiz.
    135 As razes tm um projeto dinmico para oferecer compatibilidade a casos de uso como diversas contas,
    136 dispositivos de armazenamento USB transitrios ou login/logout do usurio.</li>
    137 
    138 <li>Sob cada raiz h um documento nico. Esse documento indica 1 a <em>N</em> documentos,
    139 cada um deles, por sua vez, podem indicar 1 a <em>N</em> documentos. </li>
    140 
    141 <li>Cada back-end de armazenamento apresenta
    142 arquivos e diretrios individuais referenciando-os com um
    143 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} exclusivo.
    144 IDs de documentos devem ser exclusivos e no podem mudar depois de emitidos, pois so usados para concesses persistentes
    145 da URI em reinicializaes do dispositivo.</li>
    146 
    147 
    148 <li>Documentos podem ser um arquivo ou um diretrio que pode ser aberto (com um tipo MIME especfico)
    149  contendo documentos adicionais (com
    150 o tipo MIME{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR}).</li>
    151 
    152 <li>Cada documento tem diferentes recursos, como descrito por
    153 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}.
    154 Por exemplo,{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE},
    155 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE}
    156 e {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}.
    157 O mesmo {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} pode ser
    158 includo em diversos diretrios.</li>
    159 </ul>
    160 
    161 <h2 id="flow">Controle de fluxo</h2>
    162 <p>Como indicado anteriormente, o modelo de dados do provedor de documentos se baseia
    163 em uma hierarquia de arquivo tradicional. Contudo,  possvel armazenar os dados fisicamente como quiser desde
    164 que eles possam ser acessados pela API {@link android.provider.DocumentsProvider}. Por exemplo: seria
    165 possvel usar armazenamento em nuvem com base em tag para os dados.</p>
    166 
    167 <p>A figura 2 ilustra um exemplo de um aplicativo de foto que usa a SAF
    168 para acessar dados armazenados:</p>
    169 <p><img src="{@docRoot}images/providers/storage_dataflow.png" alt="app" /></p>
    170 
    171 <p class="img-caption"><strong>Figura 2.</strong> Fluxo da estrutura de acesso ao armazenamento</p>
    172 
    173 <p>Observe o seguinte:</p>
    174 <ul>
    175 
    176 <li>Na SAF, provedores e clientes no interagem
    177 diretamente. O cliente solicita permisso para interagir
    178 com arquivos (ou seja, para ler, editar, criar ou excluir arquivos).</li>
    179 
    180 <li>A interao comea quando um aplicativo (neste exemplo, um aplicativo de foto) dispara a inteno
    181 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} ou {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. A inteno pode conter filtros
    182 para refinar ainda mais os critrios &mdash; por exemplo: "quero todos os arquivos que podem ser abertos
    183 e que tenham o tipo MIME de tal imagem".</li>
    184 
    185 <li>Ao disparar a inteno, o seletor do sistema contata cada provedor registrado
    186 e exibe as razes de contedo correspondentes ao usurio.</li>
    187 
    188 <li>O seletor fornece aos usurios uma interface padro para acessar documentos,
    189 embora os provedores de documentos subjacentes possam ser bem diferentes. Por exemplo: a figura 2
    190 exibe um provedor do Google Drive, um provedor USB e um provedor de nuvem.</li>
    191 </ul>
    192 
    193 <p>A figura 3 exibe um seletor em que um usurio em busca de imagens selecionou
    194 uma conta do 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>Figura 3.</strong> Seletor</p>
    199 
    200 <p>Quando o usurio seleciona o Google Drive, as imagens so exibidas como ilustrado
    201 na figura 4. Desse momento em diante, o usurio poder interagir com eles de todos os modos
    202 compatveis com o provedor e o aplicativo cliente.
    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>Figura 4.</strong> Imagens</p>
    207 
    208 <h2 id="client">Programao de um aplicativo cliente</h2>
    209 
    210 <p>No Android 4.3 e em verses anteriores, para o aplicativo recuperar um arquivo de outro
    211 aplicativo, ele precisa chamar uma inteno como {@link android.content.Intent#ACTION_PICK}
    212 ou {@link android.content.Intent#ACTION_GET_CONTENT}. Em seguida, o usurio deve selecionar
    213 um nico aplicativo do qual deseja retirar um arquivo e o aplicativo selecionado deve fornecer uma interface
    214 do usurio para a busca e a seleo dos arquivos disponveis. </p>
    215 
    216 <p>No Android 4.4 e em verses posteriores, existe a opo adicional de usar
    217 a inteno {@link android.content.Intent#ACTION_OPEN_DOCUMENT},
    218 que exibe uma IU do seletor controlada pelo sistema que permite ao usurio
    219 buscar todos os arquivos que outros aplicativos disponibilizaram. Nessa IU exclusiva,
    220 o usurio pode selecionar um arquivo de qualquer aplicativo compatvel.</p>
    221 
    222 <p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    223 no substitui {@link android.content.Intent#ACTION_GET_CONTENT}.
    224 O uso de cada um deles depende da necessidade do aplicativo:</p>
    225 
    226 <ul>
    227 <li>Use {@link android.content.Intent#ACTION_GET_CONTENT} se deseja que o aplicativo
    228 simplesmente leia ou importe dados. Nessa abordagem, o aplicativo importa uma cpia dos dados,
    229 assim como um arquivo de imagem.</li>
    230 
    231 <li>Use {@link android.content.Intent#ACTION_OPEN_DOCUMENT} se deseja que o aplicativo
    232 tenha acesso persistente e de longo prazo a documentos de propriedade de um provedor
    233 de documentos. Um exemplo seria um aplicativo de edio de fotos que permite aos usurios editar
    234 imagens armazenadas em um provedor de documentos. </li>
    235 
    236 </ul>
    237 
    238 
    239 <p>Esta seo descreve como programar aplicativos clientes com base nas intenes
    240 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    241 e {@link android.content.Intent#ACTION_CREATE_DOCUMENT}.</p>
    242 
    243 
    244 <h3 id="search">Busca de documentos</h3>
    245 
    246 <p>
    247 O fragmento a seguir usa {@link android.content.Intent#ACTION_OPEN_DOCUMENT}
    248 para buscar provedores de documentos
    249 que contenham arquivos de imagem:</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>Observe o seguinte:</p>
    276 <ul>
    277 <li>Quando o aplicativo dispara a inteno
    278 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}, ele aciona um seletor que exibe todos os provedores de documentos compatveis.</li>
    279 
    280 <li>A adio da categoria {@link android.content.Intent#CATEGORY_OPENABLE}
    281  inteno filtra os resultados, que exibem somente documentos que podem ser abertos, como os arquivos de imagem.</li>
    282 
    283 <li>A declarao {@code intent.setType("image/*")} filtra ainda mais
    284 para exibir somente documentos que tm o tipo de dados MIME da imagem.</li>
    285 </ul>
    286 
    287 <h3 id="results">Processamento de resultados</h3>
    288 
    289 <p>Quando o usurio seleciona um documento no seletor,
    290 {@link android.app.Activity#onActivityResult onActivityResult()}  chamado.
    291 A URI direcionada ao documento selecionado est presente no parmetro
    292 {@code resultData}. Extraia a URI usando {@link android.content.Intent#getData getData()}.
    293 Depois, ser possvel us-la para recuperar o documento que o usurio deseja. Por
    294 exemplo:</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">Examinao de metadados de documentos</h3>
    320 
    321 <p>Quando tiver a URI de um documento, voc ter acesso aos seus metadados. Este
    322 fragmento apanha os metadados de um documento especificado pela URI e registra-os:</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">Abertura de um documento</h3>
    367 
    368 <p>Assim que tiver a URI de um documento, voc poder abri-lo ou fazer o que
    369 quiser com ele.</p>
    370 
    371 <h4>Bitmap</h4>
    372 
    373 <p>A seguir h um exemplo de como abrir um {@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>Observe que no se deve realizar essa operao no encadeamento da IU. Faa isso
    386 em segundo plano usando {@link android.os.AsyncTask}. Assim que abrir o bitmap, voc
    387 poder exibi-lo em uma {@link android.widget.ImageView}.
    388 </p>
    389 
    390 <h4>Obter uma InputStream</h4>
    391 
    392 <p>Abaixo h um exemplo de como obter uma {@link java.io.InputStream} dessa URI.
    393 Neste fragmento, as linhas do arquivo esto sendo lidas em uma string:</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">Criao de um novo documento</h3>
    411 
    412 <p>O aplicativo pode criar um novo documento em um provedor de documentos usando
    413 a inteno
    414 {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. Para criar um arquivo, deve-se fornecer um tipo MIME e um nome para o arquivo  inteno
    415 e ativ-la com um cdigo de solicitao exclusivo. O resto voc no precisa fazer:</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>Ao criar um novo documento,  possvel obter sua URI
    444 em {@link android.app.Activity#onActivityResult onActivityResult()} para que
    445 seja possvel continuar a gravar nele.</p>
    446 
    447 <h3 id="delete">Excluso de um documento</h3>
    448 
    449 <p>Se voc tem uma URI de um documento
    450 e os {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} do documento
    451  contm
    452 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},
    453 voc pode excluir o documento. Por exemplo:</p>
    454 
    455 <pre>
    456 DocumentsContract.deleteDocument(getContentResolver(), uri);
    457 </pre>
    458 
    459 <h3 id="edit">Edio de um documento</h3>
    460 
    461 <p>Voc pode usar a SAF para editar o documento de texto no local em que est armazenado.
    462 Esse fragmento dispara
    463 a inteno {@link android.content.Intent#ACTION_OPEN_DOCUMENT} e usa
    464 a categoria {@link android.content.Intent#CATEGORY_OPENABLE} para exibir somente
    465 documentos que possam ser abertos. Ela filtra ainda mais para exibir somente arquivos de texto:</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>Em seguida, de {@link android.app.Activity#onActivityResult onActivityResult()}
    489 (consulte <a href="#results">Processar resultados</a>), voc pode chamar o cdigo para realizar a edio.
    490 O fragmento a seguir obtm um {@link java.io.FileOutputStream}
    491 do {@link android.content.ContentResolver}. Por padro, ele usa o modo de "gravao".
    492 Recomenda-se solicitar a menor quantidade de acesso necessria, portanto no solicite
    493 acesso de leitura e programao se s for necessria a programao:</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">Manuteno de permisses</h3>
    514 
    515 <p>Quando o aplicativo abre um arquivo para leitura ou programao, o sistema lhe fornece
    516 uma concesso da permisso da URI para tal arquivo. Ela vigora at a reinicializao do dispositivo do usurio.
    517 Contudo, suponhamos que seu aplicativo seja de edio de imagens e voc queira que os usurios
    518 acessem as ltimas 5 imagens que editaram diretamente do aplicativo. Se o dispositivo do usurio
    519 reiniciou, voc teria que enviar o usurio de volta ao seletor do sistema para encontrar
    520 os arquivos, o que, obviamente, no  o ideal.</p>
    521 
    522 <p>Para evitar que isso acontea, voc pode manter as permisses que o sistema
    523 forneceu ao aplicativo. Efetivamente, o aplicativo "toma" a concesso de permisso da URI persistente
    524 que o sistema est oferecendo. Isso concede ao usurio um acesso contnuo aos arquivos
    525 por meio do aplicativo mesmo se o dispositivo for reiniciado:</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>H uma etapa final. Voc pode ter salvo as URIs
    535 mais recentes acessadas pelo seu aplicativo, mas elas no sero mais vlidas &mdash; outro aplicativo
    536 pode ter excludo ou modificado um documento. Portanto, deve-se sempre chamar
    537 {@code getContentResolver().takePersistableUriPermission()} para verificar
    538 se h dados mais recentes.</p>
    539 
    540 <h2 id="custom">Criao de um provedor de documentos personalizado</h2>
    541 
    542 <p>
    543 Se voc est desenvolvimento um aplicativo que fornece servios de armazenamento para arquivos (como
    544 um servio de armazenamento em nuvem),  possvel disponibilizar os arquivos
    545 pela SAF criando um provedor de documentos personalizado.  Esta seo mostra
    546 como faz-lo.</p>
    547 
    548 
    549 <h3 id="manifest">Manifesto</h3>
    550 
    551 <p>Para implementar um provedor de documentos personalizado, adicione ao manifesto
    552 do aplicativo:</p>
    553 <ul>
    554 
    555 <li>Um alvo de API de nvel 19 ou posterior.</li>
    556 
    557 <li>Um elemento <code>&lt;provider&gt;</code> que declare o provedor de armazenamento
    558 personalizado. </li>
    559 
    560 <li>O nome do provedor, que  o nome da classe, inclusive o nome do pacote.
    561 Por exemplo: <code>com.example.android.storageprovider.MyCloudProvider</code>.</li>
    562 
    563 <li>O nome da autoridade, que  o nome do pacote (neste exemplo,
    564 <code>com.example.android.storageprovider</code>) e o tipo de provedor de contedo
    565 (<code>documents</code>). Por exemplo: {@code com.example.android.storageprovider.documents}.</li>
    566 
    567 <li>O atributo <code>android:exported</code> definido como <code>&quot;true&quot;</code>.
    568  necessrio exportar o provedor para que outros aplicativos possam v-lo.</li>
    569 
    570 <li>O atributo <code>android:grantUriPermissions</code> definido como
    571 <code>&quot;true&quot;</code>. Essa configurao permite ao sistema conceder acesso ao contedo
    572 no provedor a outros aplicativos. Para ver como manter uma concesso
    573 para determinado documento, consulte <a href="#permissions">Manuteno de permisses</a>.</li>
    574 
    575 <li>A permisso {@code MANAGE_DOCUMENTS}. Por padro, um provedor est disponvel
    576 para todos. A adio dessa permisso restringir o provedor em relao ao sistema.
    577 Essa restrio  importante em termos de segurana.</li>
    578 
    579 <li>O atributo {@code android:enabled} definido como um valor booleano determinado em um arquivo
    580 de recursos. Esse atributo visa desativar o provedor em dispositivos que executam o Android 4.3 ou verses anteriores.
    581 Por exemplo: {@code android:enabled="@bool/atLeastKitKat"}. Alm
    582 disso, para incluir esse atributo no manifesto, deve-se fazer o seguinte:
    583 <ul>
    584 <li>No arquivo de recursos {@code bool.xml} em {@code res/values/}, adicione
    585 esta linha: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;false&lt;/bool&gt;</pre></li>
    586 
    587 <li>No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione
    588 esta linha: <pre>&lt;bool name=&quot;atLeastKitKat&quot;&gt;true&lt;/bool&gt;</pre></li>
    589 </ul></li>
    590 
    591 <li>Um filtro de intenes que contenha
    592 a ao {@code android.content.action.DOCUMENTS_PROVIDER} para que o provedor
    593 aparea no seletor quando o sistema procurar provedores.</li>
    594 
    595 </ul>
    596 <p>Abaixo h alguns excertos de amostra de um manifesto que contm um provedor:</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">Compatibilidade com dispositivos que executam Android 4.3 ou anterior</h4>
    620 
    621 <p>
    622 A inteno {@link android.content.Intent#ACTION_OPEN_DOCUMENT} est disponvel somente
    623 em dispositivos que executam o Android 4.4 ou posteriores.
    624 Se voc deseja que o aplicativo seja compatvel com {@link android.content.Intent#ACTION_GET_CONTENT}
    625 para adaptar-se a dispositivos que executam o Android 4.3 ou verses anteriores,  necessrio
    626 desativar o filtro de inteno {@link android.content.Intent#ACTION_GET_CONTENT}
    627 no manifesto para dispositivos que executam Android 4.4 ou verses posteriores.
    628 Um provedor de documentos e {@link android.content.Intent#ACTION_GET_CONTENT} devem ser avaliados
    629 de forma mutuamente exclusiva. Se houver compatibilidade com ambos simultaneamente, o aplicativo
    630 aparecer duas vezes na IU do seletor do sistema, oferecendo dois meios de acesso
    631 diferentes aos dados armazenados. Isso pode confundir os usurios.</p>
    632 
    633 <p>A seguir apresenta-se a forma recomendada de desativar
    634 o filtro de intenes {@link android.content.Intent#ACTION_GET_CONTENT} para dispositivos
    635 que executam o Android 4.4 ou verses posteriores:</p>
    636 
    637 <ol>
    638 <li>No arquivo de recursos {@code bool.xml} em {@code res/values/}, adicione
    639 esta linha: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;true&lt;/bool&gt;</pre></li>
    640 
    641 <li>No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione
    642 esta linha: <pre>&lt;bool name=&quot;atMostJellyBeanMR2&quot;&gt;false&lt;/bool&gt;</pre></li>
    643 
    644 <li>Adicione
    645 um <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html">alias
    646 de atividade</a> para desativar o filtro de intenes
    647 {@link android.content.Intent#ACTION_GET_CONTENT} para verses 4.4 (API de nvel 19) e posteriores. Por exemplo:
    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">Contratos</h3>
    668 
    669 <p>Normalmente, ao criar um provedor de contedo personalizado, uma das tarefas
    670  implementar classes de contrato, como descrito
    671 no guia dos desenvolvedores de<a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContractClass">
    672 Provedores de contedo</a>. Classe de contrato  uma classe {@code public final}
    673 que contm definies constantes das URIs, nomes de coluna, tipos MIME
    674 e outros metadados que pertencem ao provedor. A SAF
    675 fornece essas classes de contrato, portanto no  necessrio
    676 program-las:</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>Por exemplo, eis as colunas que podem retornar em um cursor
    684 ao consultar documentos ou a raiz do provedor de documentos:</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">Subclasse DocumentsProvider</h3>
    698 
    699 <p>A prxima etapa na criao de um provedor de documentos personalizado  atribuir uma subclasse 
    700 classe {@link android.provider.DocumentsProvider} abstrata. No mnimo,  necessrio
    701 implementar os seguintes mtodos:</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>Esses so os nicos mtodos que obrigatoriamente devem ser implementados, mas h
    714 muitos outros que podem ser usados. Consulte {@link android.provider.DocumentsProvider}
    715 para ver mais detalhes.</p>
    716 
    717 <h4 id="queryRoots">Implementao de queryRoots</h4>
    718 
    719 <p>A implementao de {@link android.provider.DocumentsProvider#queryRoots
    720 queryRoots()} deve retornar um {@link android.database.Cursor} que aponta para todos
    721 os diretrios raiz dos provedores de documentos, usando colunas definidas
    722 em {@link android.provider.DocumentsContract.Root}.</p>
    723 
    724 <p>No fragmento a seguir, o parmetro {@code projection} representa
    725 os campos especficos que o autor da chamada quer receber de volta. O fragmento cria um novo cursor
    726 e adiciona-lhe uma linha &mdash; uma raiz, um diretrio de nvel superior, como
    727 Downloads ou Imagens.  A maioria dos provedores tem somente uma raiz.  possvel ter mais de uma,
    728 por exemplo, no caso de diversas contas de usurio. Nesse caso, adicione somente
    729 uma segunda linha ao cursor.</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">Implementao de queryChildDocuments</h4>
    778 
    779 <p>A implementao
    780 de {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
    781 deve retornar um {@link android.database.Cursor} que aponta para todos os arquivos
    782 no diretrio especificado com colunas definidas em
    783 {@link android.provider.DocumentsContract.Document}.</p>
    784 
    785 <p>Esse mtodo  chamado quando uma raiz do aplicativo  escolhida na IU do seletor.
    786 Ele coleta os documentos filhos de um diretrio na raiz.  Ele pode ser chamado em qualquer nvel
    787 na hierarquia de arquivos, no somente na raiz. Esse fragmento
    788 cria um novo cursor com as colunas solicitadas e, em seguida, adiciona informaes ao cursor
    789 sobre cada filho imediato no diretrio pai.
    790 O filho pode ser uma imagem, outro diretrio &mdash; qualquer arquivo:</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">Implementao de queryDocument</h4>
    808 
    809 <p>A implementao de
    810 {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    811 deve retornar um {@link android.database.Cursor} que aponta para o arquivo especificado
    812 com colunas definidas em {@link android.provider.DocumentsContract.Document}.
    813 </p>
    814 
    815 <p>O mtodo {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    816 retorna as mesmas informaes passadas em
    817 {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()},
    818 mas para um arquivo especfico.</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">Implementao de openDocument</h4>
    834 
    835 <p>Deve-se implementar {@link android.provider.DocumentsProvider#openDocument
    836 openDocument()} para retornar um {@link android.os.ParcelFileDescriptor} que represente
    837 o arquivo especificado. Outros aplicativos podem usar o {@link android.os.ParcelFileDescriptor} retornado
    838 para transmitir dados. O sistema chama esse mtodo quando o usurio seleciona um arquivo
    839 e o aplicativo cliente solicita acesso a ele chamando
    840 {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}.
    841 Por exemplo:</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">Segurana</h3>
    886 
    887 <p>Suponha que o provedor de documentos seja um servio de armazenamento em nuvem protegido por senha
    888 e que voc queira certificar-se de que os usurios estejam conectados antes de iniciar o compartilhamento dos arquivos.
    889 O que o aplicativo deve fazer se o usurio no estiver conectado?  A soluo  retornar
    890 zero raiz na implementao de {@link android.provider.DocumentsProvider#queryRoots
    891 queryRoots()}, ou seja, um cursor de raiz vazio:</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>A outra etapa  chamar {@code getContentResolver().notifyChange()}.
    904 Lembra-se do {@link android.provider.DocumentsContract}?  Estamos usando-o para criar
    905 esta URI. O fragmento a seguir pede ao sistema que consulte as razes
    906 do provedor de documentos sempre que o status de login do usurio mudar. Se o usurio no estiver
    907 conectado, uma chamada de {@link android.provider.DocumentsProvider#queryRoots queryRoots()} retornar um
    908 cursor vazio, como exibido anteriormente. Isso garante que os documentos do provedor estejam
    909 disponveis somente se o usurio tiver acesso ao provedor.</p>
    910 
    911 <pre>private void onLoginButtonClick() {
    912     loginOrLogout();
    913     getContentResolver().notifyChange(DocumentsContract
    914             .buildRootsUri(AUTHORITY), null);
    915 }
    916 </pre>