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> — 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> — 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> — 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 — 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 "file chooser" 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 "opened", 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 "audio/ogg". 268 // To search for all documents available via installed storage providers, 269 // it would be "*/*". 270 intent.setType("image/*"); 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>@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 // "if there's anything to look at, look at it" conditionals. 335 if (cursor != null && cursor.moveToFirst()) { 336 337 // Note it's called "Display Name". 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, "Display Name: " + 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 "unpredictable". 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 = "Unknown"; 357 } 358 Log.i(TAG, "Size: " + 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 "opened", 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 "opened", 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("text/plain"); 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 & (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 — 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><provider></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>"true"</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>"true"</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><bool name="atLeastKitKat">false</bool></pre></li> 586 587 <li>No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione 588 esta linha: <pre><bool name="atLeastKitKat">true</bool></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><manifest... > 599 ... 600 <uses-sdk 601 android:minSdkVersion="19" 602 android:targetSdkVersion="19" /> 603 .... 604 <provider 605 android:name="com.example.android.storageprovider.MyCloudProvider" 606 android:authorities="com.example.android.storageprovider.documents" 607 android:grantUriPermissions="true" 608 android:exported="true" 609 android:permission="android.permission.MANAGE_DOCUMENTS" 610 android:enabled="@bool/atLeastKitKat"> 611 <intent-filter> 612 <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> 613 </intent-filter> 614 </provider> 615 </application> 616 617 </manifest></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><bool name="atMostJellyBeanMR2">true</bool></pre></li> 640 641 <li>No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione 642 esta linha: <pre><bool name="atMostJellyBeanMR2">false</bool></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 <!-- This activity alias is added so that GET_CONTENT intent-filter 651 can be disabled for builds on API level 19 and higher. --> 652 <activity-alias android:name="com.android.example.app.MyPicker" 653 android:targetActivity="com.android.example.app.MyActivity" 654 ... 655 android:enabled="@bool/atMostJellyBeanMR2"> 656 <intent-filter> 657 <action android:name="android.intent.action.GET_CONTENT" /> 658 <category android:name="android.intent.category.OPENABLE" /> 659 <category android:name="android.intent.category.DEFAULT" /> 660 <data android:mimeType="image/*" /> 661 <data android:mimeType="video/*" /> 662 </intent-filter> 663 </activity-alias> 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 — 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 @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 "MyCloud". 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 "Recents" 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 — qualquer arquivo:</p> 791 792 <pre>@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>@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>@Override 844 public ParcelFileDescriptor openDocument(final String documentId, 845 final String mode, 846 CancellationSignal signal) throws 847 FileNotFoundException { 848 Log.v(TAG, "openDocument, mode: " + 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 @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, "A file with id " + 869 documentId + " has been closed! 870 Time to " + 871 "update the server."); 872 } 873 874 }); 875 } catch (IOException e) { 876 throw new FileNotFoundException("Failed to open document with id " 877 + documentId + " and mode " + 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>