1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.apis.content; 18 19 import android.app.Activity; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.os.Bundle; 26 import android.os.Environment; 27 import android.os.storage.StorageManager; 28 import android.os.storage.StorageVolume; 29 import android.provider.DocumentsContract; 30 import android.provider.DocumentsContract.Document; 31 import android.util.Log; 32 import android.view.View; 33 import android.view.View.OnClickListener; 34 import android.widget.Button; 35 import android.widget.CheckBox; 36 import android.widget.LinearLayout; 37 import android.widget.ScrollView; 38 import android.widget.TextView; 39 40 import java.io.ByteArrayOutputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.OutputStream; 44 45 /** 46 * Example that exercises client side of {@link DocumentsContract}. 47 */ 48 public class DocumentsSample extends Activity { 49 private static final String TAG = "DocumentsSample"; 50 51 private static final int CODE_READ = 42; 52 private static final int CODE_WRITE = 43; 53 private static final int CODE_TREE = 44; 54 private static final int CODE_RENAME = 45; 55 56 private TextView mResult; 57 58 @Override onCreate(Bundle icicle)59 public void onCreate(Bundle icicle) { 60 super.onCreate(icicle); 61 62 final Context context = this; 63 64 final LinearLayout view = new LinearLayout(context); 65 view.setOrientation(LinearLayout.VERTICAL); 66 67 mResult = new TextView(context); 68 view.addView(mResult); 69 70 final CheckBox multiple = new CheckBox(context); 71 multiple.setText("ALLOW_MULTIPLE"); 72 view.addView(multiple); 73 final CheckBox localOnly = new CheckBox(context); 74 localOnly.setText("LOCAL_ONLY"); 75 view.addView(localOnly); 76 77 Button button; 78 button = new Button(context); 79 button.setText("OPEN_DOC */*"); 80 button.setOnClickListener(new OnClickListener() { 81 @Override 82 public void onClick(View v) { 83 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 84 intent.addCategory(Intent.CATEGORY_OPENABLE); 85 intent.setType("*/*"); 86 if (multiple.isChecked()) { 87 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 88 } 89 if (localOnly.isChecked()) { 90 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 91 } 92 startActivityForResult(intent, CODE_READ); 93 } 94 }); 95 view.addView(button); 96 97 button = new Button(context); 98 button.setText("OPEN_DOC image/*"); 99 button.setOnClickListener(new OnClickListener() { 100 @Override 101 public void onClick(View v) { 102 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 103 intent.addCategory(Intent.CATEGORY_OPENABLE); 104 intent.setType("image/*"); 105 if (multiple.isChecked()) { 106 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 107 } 108 if (localOnly.isChecked()) { 109 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 110 } 111 startActivityForResult(intent, CODE_READ); 112 } 113 }); 114 view.addView(button); 115 116 button = new Button(context); 117 button.setText("OPEN_DOC audio/ogg"); 118 button.setOnClickListener(new OnClickListener() { 119 @Override 120 public void onClick(View v) { 121 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 122 intent.addCategory(Intent.CATEGORY_OPENABLE); 123 intent.setType("audio/ogg"); 124 if (multiple.isChecked()) { 125 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 126 } 127 if (localOnly.isChecked()) { 128 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 129 } 130 startActivityForResult(intent, CODE_READ); 131 } 132 }); 133 view.addView(button); 134 135 button = new Button(context); 136 button.setText("OPEN_DOC text/plain, application/msword"); 137 button.setOnClickListener(new OnClickListener() { 138 @Override 139 public void onClick(View v) { 140 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 141 intent.addCategory(Intent.CATEGORY_OPENABLE); 142 intent.setType("*/*"); 143 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { 144 "text/plain", "application/msword" }); 145 if (multiple.isChecked()) { 146 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 147 } 148 if (localOnly.isChecked()) { 149 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 150 } 151 startActivityForResult(intent, CODE_READ); 152 } 153 }); 154 view.addView(button); 155 156 button = new Button(context); 157 button.setText("CREATE_DOC text/plain"); 158 button.setOnClickListener(new OnClickListener() { 159 @Override 160 public void onClick(View v) { 161 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); 162 intent.addCategory(Intent.CATEGORY_OPENABLE); 163 intent.setType("text/plain"); 164 intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt"); 165 if (localOnly.isChecked()) { 166 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 167 } 168 startActivityForResult(intent, CODE_WRITE); 169 } 170 }); 171 view.addView(button); 172 173 button = new Button(context); 174 button.setText("CREATE_DOC image/png"); 175 button.setOnClickListener(new OnClickListener() { 176 @Override 177 public void onClick(View v) { 178 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); 179 intent.addCategory(Intent.CATEGORY_OPENABLE); 180 intent.setType("image/png"); 181 intent.putExtra(Intent.EXTRA_TITLE, "mypicture.png"); 182 if (localOnly.isChecked()) { 183 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 184 } 185 startActivityForResult(intent, CODE_WRITE); 186 } 187 }); 188 view.addView(button); 189 190 button = new Button(context); 191 button.setText("GET_CONTENT */*"); 192 button.setOnClickListener(new OnClickListener() { 193 @Override 194 public void onClick(View v) { 195 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 196 intent.addCategory(Intent.CATEGORY_OPENABLE); 197 intent.setType("*/*"); 198 if (multiple.isChecked()) { 199 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 200 } 201 if (localOnly.isChecked()) { 202 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 203 } 204 startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_READ); 205 } 206 }); 207 view.addView(button); 208 209 button = new Button(context); 210 button.setText("GET_CONTENT */* with content query \"FILE\""); 211 button.setOnClickListener(new OnClickListener() { 212 @Override 213 public void onClick(View v) { 214 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 215 intent.addCategory(Intent.CATEGORY_OPENABLE); 216 intent.setType("*/*"); 217 intent.putExtra(Intent.EXTRA_CONTENT_QUERY, "FILE"); 218 if (multiple.isChecked()) { 219 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 220 } 221 if (localOnly.isChecked()) { 222 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 223 } 224 startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_READ); 225 } 226 }); 227 view.addView(button); 228 229 button = new Button(context); 230 button.setText("OPEN_DOC_TREE"); 231 button.setOnClickListener(new OnClickListener() { 232 @Override 233 public void onClick(View v) { 234 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 235 if (localOnly.isChecked()) { 236 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 237 } 238 startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_TREE); 239 } 240 }); 241 view.addView(button); 242 243 button = new Button(context); 244 button.setText("OPEN_DOC_TREE primary volume initial uri"); 245 button.setOnClickListener(new OnClickListener() { 246 @Override 247 public void onClick(View v) { 248 Intent intent = getSystemService(StorageManager.class) 249 .getPrimaryStorageVolume() 250 .createOpenDocumentTreeIntent(); 251 startActivityForResult(intent, CODE_TREE); 252 } 253 }); 254 view.addView(button); 255 256 button = new Button(context); 257 button.setText("OPEN_DOC */* for rename"); 258 button.setOnClickListener(new OnClickListener() { 259 @Override 260 public void onClick(View v) { 261 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 262 intent.addCategory(Intent.CATEGORY_OPENABLE); 263 intent.setType("*/*"); 264 startActivityForResult(intent, CODE_RENAME); 265 } 266 }); 267 view.addView(button); 268 269 final ScrollView scroll = new ScrollView(context); 270 scroll.addView(view); 271 272 setContentView(scroll); 273 } 274 275 @Override onActivityResult(int requestCode, int resultCode, Intent data)276 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 277 final ContentResolver cr = getContentResolver(); 278 279 clearLog(); 280 281 log("resultCode=" + resultCode); 282 log("data=" + String.valueOf(data)); 283 284 final Uri uri = data != null ? data.getData() : null; 285 if (uri != null) { 286 log("isDocumentUri=" + DocumentsContract.isDocumentUri(this, uri)); 287 } else { 288 log("missing URI?"); 289 return; 290 } 291 292 if (requestCode == CODE_READ) { 293 try { 294 cr.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 295 } catch (SecurityException e) { 296 log("FAILED TO TAKE PERMISSION", e); 297 } 298 InputStream is = null; 299 try { 300 is = cr.openInputStream(uri); 301 log("read length=" + readFullyNoClose(is).length); 302 } catch (Exception e) { 303 log("FAILED TO READ", e); 304 } finally { 305 closeQuietly(is); 306 } 307 } else if (requestCode == CODE_WRITE) { 308 try { 309 cr.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 310 } catch (SecurityException e) { 311 log("FAILED TO TAKE PERMISSION", e); 312 } 313 OutputStream os = null; 314 try { 315 os = cr.openOutputStream(uri); 316 os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes()); 317 log("wrote data"); 318 } catch (Exception e) { 319 log("FAILED TO WRITE", e); 320 } finally { 321 closeQuietly(os); 322 } 323 } else if (requestCode == CODE_TREE) { 324 try { 325 cr.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 326 } catch (SecurityException e) { 327 log("FAILED TO TAKE PERMISSION", e); 328 } 329 // Find existing docs 330 Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri, 331 DocumentsContract.getTreeDocumentId(uri)); 332 Uri child = DocumentsContract.buildChildDocumentsUriUsingTree(uri, 333 DocumentsContract.getTreeDocumentId(uri)); 334 Cursor c = cr.query(child, new String[] { 335 Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE }, null, null, null); 336 try { 337 while (c.moveToNext()) { 338 log("found child=" + c.getString(0) + ", mime=" + c.getString(1)); 339 } 340 } finally { 341 closeQuietly(c); 342 } 343 344 // Create some documents 345 Uri pic = createDocument(cr, doc, "image/png", "pic.png"); 346 Uri dir = createDocument(cr, doc, Document.MIME_TYPE_DIR, "my dir"); 347 Uri dirPic = createDocument(cr, dir, "image/png", "pic2.png"); 348 349 log("created " + pic); 350 log("created " + dir); 351 log("created " + dirPic); 352 353 // Write to one of them 354 OutputStream os = null; 355 try { 356 os = cr.openOutputStream(dirPic); 357 os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes()); 358 log("wrote data"); 359 } catch (Exception e) { 360 log("FAILED TO WRITE", e); 361 } finally { 362 closeQuietly(os); 363 } 364 365 // And delete the first pic 366 if (deleteDocument(cr, pic)) { 367 log("deleted untouched pic"); 368 } else { 369 log("FAILED TO DELETE PIC"); 370 } 371 } else if (requestCode == CODE_RENAME) { 372 final Uri newUri = renameDocument(cr, uri, "MEOW.TEST"); 373 log("rename result=" + newUri); 374 375 InputStream is = null; 376 try { 377 is = cr.openInputStream(newUri); 378 log("read length=" + readFullyNoClose(is).length); 379 } catch (Exception e) { 380 log("FAILED TO READ", e); 381 } finally { 382 closeQuietly(is); 383 } 384 } 385 } 386 createDocument(ContentResolver resolver, Uri documentUri, String mimeType, String displayName)387 private Uri createDocument(ContentResolver resolver, Uri documentUri, String mimeType, 388 String displayName) { 389 Uri uri; 390 try { 391 uri = DocumentsContract.createDocument(resolver, documentUri, mimeType, displayName); 392 } catch (Exception e) { 393 uri = null; 394 } 395 return uri; 396 } 397 deleteDocument(ContentResolver resolver, Uri documentUri)398 private boolean deleteDocument(ContentResolver resolver, Uri documentUri) { 399 try { 400 return DocumentsContract.deleteDocument(resolver, documentUri); 401 } catch (Exception e) { 402 return false; 403 } 404 } 405 renameDocument(ContentResolver resolver, Uri uri, String newName)406 private Uri renameDocument(ContentResolver resolver, Uri uri, String newName) { 407 try { 408 return DocumentsContract.renameDocument(resolver, uri, newName); 409 } catch (Exception e) { 410 return null; 411 } 412 } 413 clearLog()414 private void clearLog() { 415 mResult.setText(null); 416 } 417 log(String msg)418 private void log(String msg) { 419 log(msg, null); 420 } 421 log(String msg, Throwable t)422 private void log(String msg, Throwable t) { 423 Log.d(TAG, msg, t); 424 mResult.setText(mResult.getText() + "\n" + msg); 425 } 426 readFullyNoClose(InputStream in)427 public static byte[] readFullyNoClose(InputStream in) throws IOException { 428 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 429 byte[] buffer = new byte[1024]; 430 int count; 431 while ((count = in.read(buffer)) != -1) { 432 bytes.write(buffer, 0, count); 433 } 434 return bytes.toByteArray(); 435 } 436 closeQuietly(AutoCloseable closeable)437 public static void closeQuietly(AutoCloseable closeable) { 438 if (closeable != null) { 439 try { 440 closeable.close(); 441 } catch (RuntimeException rethrown) { 442 throw rethrown; 443 } catch (Exception ignored) { 444 } 445 } 446 } 447 } 448