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.provider.DocumentsContract; 27 import android.provider.DocumentsContract.Document; 28 import android.util.Log; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.widget.Button; 32 import android.widget.CheckBox; 33 import android.widget.LinearLayout; 34 import android.widget.ScrollView; 35 import android.widget.TextView; 36 37 import java.io.ByteArrayOutputStream; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.io.OutputStream; 41 42 /** 43 * Example that exercises client side of {@link DocumentsContract}. 44 */ 45 public class DocumentsSample extends Activity { 46 private static final String TAG = "DocumentsSample"; 47 48 private static final int CODE_READ = 42; 49 private static final int CODE_WRITE = 43; 50 private static final int CODE_TREE = 44; 51 private static final int CODE_RENAME = 45; 52 53 private TextView mResult; 54 55 @Override 56 public void onCreate(Bundle icicle) { 57 super.onCreate(icicle); 58 59 final Context context = this; 60 61 final LinearLayout view = new LinearLayout(context); 62 view.setOrientation(LinearLayout.VERTICAL); 63 64 mResult = new TextView(context); 65 view.addView(mResult); 66 67 final CheckBox multiple = new CheckBox(context); 68 multiple.setText("ALLOW_MULTIPLE"); 69 view.addView(multiple); 70 final CheckBox localOnly = new CheckBox(context); 71 localOnly.setText("LOCAL_ONLY"); 72 view.addView(localOnly); 73 74 Button button; 75 button = new Button(context); 76 button.setText("OPEN_DOC */*"); 77 button.setOnClickListener(new OnClickListener() { 78 @Override 79 public void onClick(View v) { 80 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 81 intent.addCategory(Intent.CATEGORY_OPENABLE); 82 intent.setType("*/*"); 83 if (multiple.isChecked()) { 84 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 85 } 86 if (localOnly.isChecked()) { 87 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 88 } 89 startActivityForResult(intent, CODE_READ); 90 } 91 }); 92 view.addView(button); 93 94 button = new Button(context); 95 button.setText("OPEN_DOC image/*"); 96 button.setOnClickListener(new OnClickListener() { 97 @Override 98 public void onClick(View v) { 99 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 100 intent.addCategory(Intent.CATEGORY_OPENABLE); 101 intent.setType("image/*"); 102 if (multiple.isChecked()) { 103 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 104 } 105 if (localOnly.isChecked()) { 106 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 107 } 108 startActivityForResult(intent, CODE_READ); 109 } 110 }); 111 view.addView(button); 112 113 button = new Button(context); 114 button.setText("OPEN_DOC audio/ogg"); 115 button.setOnClickListener(new OnClickListener() { 116 @Override 117 public void onClick(View v) { 118 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 119 intent.addCategory(Intent.CATEGORY_OPENABLE); 120 intent.setType("audio/ogg"); 121 if (multiple.isChecked()) { 122 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 123 } 124 if (localOnly.isChecked()) { 125 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 126 } 127 startActivityForResult(intent, CODE_READ); 128 } 129 }); 130 view.addView(button); 131 132 button = new Button(context); 133 button.setText("OPEN_DOC text/plain, application/msword"); 134 button.setOnClickListener(new OnClickListener() { 135 @Override 136 public void onClick(View v) { 137 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 138 intent.addCategory(Intent.CATEGORY_OPENABLE); 139 intent.setType("*/*"); 140 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { 141 "text/plain", "application/msword" }); 142 if (multiple.isChecked()) { 143 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 144 } 145 if (localOnly.isChecked()) { 146 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 147 } 148 startActivityForResult(intent, CODE_READ); 149 } 150 }); 151 view.addView(button); 152 153 button = new Button(context); 154 button.setText("CREATE_DOC text/plain"); 155 button.setOnClickListener(new OnClickListener() { 156 @Override 157 public void onClick(View v) { 158 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); 159 intent.addCategory(Intent.CATEGORY_OPENABLE); 160 intent.setType("text/plain"); 161 intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt"); 162 if (localOnly.isChecked()) { 163 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 164 } 165 startActivityForResult(intent, CODE_WRITE); 166 } 167 }); 168 view.addView(button); 169 170 button = new Button(context); 171 button.setText("CREATE_DOC image/png"); 172 button.setOnClickListener(new OnClickListener() { 173 @Override 174 public void onClick(View v) { 175 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); 176 intent.addCategory(Intent.CATEGORY_OPENABLE); 177 intent.setType("image/png"); 178 intent.putExtra(Intent.EXTRA_TITLE, "mypicture.png"); 179 if (localOnly.isChecked()) { 180 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 181 } 182 startActivityForResult(intent, CODE_WRITE); 183 } 184 }); 185 view.addView(button); 186 187 button = new Button(context); 188 button.setText("GET_CONTENT */*"); 189 button.setOnClickListener(new OnClickListener() { 190 @Override 191 public void onClick(View v) { 192 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 193 intent.addCategory(Intent.CATEGORY_OPENABLE); 194 intent.setType("*/*"); 195 if (multiple.isChecked()) { 196 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 197 } 198 if (localOnly.isChecked()) { 199 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 200 } 201 startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_READ); 202 } 203 }); 204 view.addView(button); 205 206 button = new Button(context); 207 button.setText("OPEN_DOC_TREE"); 208 button.setOnClickListener(new OnClickListener() { 209 @Override 210 public void onClick(View v) { 211 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 212 if (localOnly.isChecked()) { 213 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 214 } 215 startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_TREE); 216 } 217 }); 218 view.addView(button); 219 220 button = new Button(context); 221 button.setText("OPEN_DOC */* for rename"); 222 button.setOnClickListener(new OnClickListener() { 223 @Override 224 public void onClick(View v) { 225 Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); 226 intent.addCategory(Intent.CATEGORY_OPENABLE); 227 intent.setType("*/*"); 228 startActivityForResult(intent, CODE_RENAME); 229 } 230 }); 231 view.addView(button); 232 233 final ScrollView scroll = new ScrollView(context); 234 scroll.addView(view); 235 236 setContentView(scroll); 237 } 238 239 @Override 240 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 241 final ContentResolver cr = getContentResolver(); 242 243 clearLog(); 244 245 log("resultCode=" + resultCode); 246 log("data=" + String.valueOf(data)); 247 248 final Uri uri = data != null ? data.getData() : null; 249 if (uri != null) { 250 log("isDocumentUri=" + DocumentsContract.isDocumentUri(this, uri)); 251 } else { 252 log("missing URI?"); 253 return; 254 } 255 256 if (requestCode == CODE_READ) { 257 try { 258 cr.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 259 } catch (SecurityException e) { 260 log("FAILED TO TAKE PERMISSION", e); 261 } 262 InputStream is = null; 263 try { 264 is = cr.openInputStream(uri); 265 log("read length=" + readFullyNoClose(is).length); 266 } catch (Exception e) { 267 log("FAILED TO READ", e); 268 } finally { 269 closeQuietly(is); 270 } 271 } else if (requestCode == CODE_WRITE) { 272 try { 273 cr.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 274 } catch (SecurityException e) { 275 log("FAILED TO TAKE PERMISSION", e); 276 } 277 OutputStream os = null; 278 try { 279 os = cr.openOutputStream(uri); 280 os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes()); 281 log("wrote data"); 282 } catch (Exception e) { 283 log("FAILED TO WRITE", e); 284 } finally { 285 closeQuietly(os); 286 } 287 } else if (requestCode == CODE_TREE) { 288 // Find existing docs 289 Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri, 290 DocumentsContract.getTreeDocumentId(uri)); 291 Uri child = DocumentsContract.buildChildDocumentsUriUsingTree(uri, 292 DocumentsContract.getTreeDocumentId(uri)); 293 Cursor c = cr.query(child, new String[] { 294 Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE }, null, null, null); 295 try { 296 while (c.moveToNext()) { 297 log("found child=" + c.getString(0) + ", mime=" + c.getString(1)); 298 } 299 } finally { 300 closeQuietly(c); 301 } 302 303 // Create some documents 304 Uri pic = createDocument(cr, doc, "image/png", "pic.png"); 305 Uri dir = createDocument(cr, doc, Document.MIME_TYPE_DIR, "my dir"); 306 Uri dirPic = createDocument(cr, dir, "image/png", "pic2.png"); 307 308 log("created " + pic); 309 log("created " + dir); 310 log("created " + dirPic); 311 312 // Write to one of them 313 OutputStream os = null; 314 try { 315 os = cr.openOutputStream(dirPic); 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 324 // And delete the first pic 325 if (deleteDocument(cr, pic)) { 326 log("deleted untouched pic"); 327 } else { 328 log("FAILED TO DELETE PIC"); 329 } 330 } else if (requestCode == CODE_RENAME) { 331 final Uri newUri = renameDocument(cr, uri, "MEOW.TEST"); 332 log("rename result=" + newUri); 333 334 InputStream is = null; 335 try { 336 is = cr.openInputStream(newUri); 337 log("read length=" + readFullyNoClose(is).length); 338 } catch (Exception e) { 339 log("FAILED TO READ", e); 340 } finally { 341 closeQuietly(is); 342 } 343 } 344 } 345 346 private Uri createDocument(ContentResolver resolver, Uri documentUri, String mimeType, 347 String displayName) { 348 Uri uri; 349 try { 350 uri = DocumentsContract.createDocument(resolver, documentUri, mimeType, displayName); 351 } catch (Exception e) { 352 uri = null; 353 } 354 return uri; 355 } 356 357 private boolean deleteDocument(ContentResolver resolver, Uri documentUri) { 358 try { 359 return DocumentsContract.deleteDocument(resolver, documentUri); 360 } catch (Exception e) { 361 return false; 362 } 363 } 364 365 private Uri renameDocument(ContentResolver resolver, Uri uri, String newName) { 366 try { 367 return DocumentsContract.renameDocument(resolver, uri, newName); 368 } catch (Exception e) { 369 return null; 370 } 371 } 372 373 private void clearLog() { 374 mResult.setText(null); 375 } 376 377 private void log(String msg) { 378 log(msg, null); 379 } 380 381 private void log(String msg, Throwable t) { 382 Log.d(TAG, msg, t); 383 mResult.setText(mResult.getText() + "\n" + msg); 384 } 385 386 public static byte[] readFullyNoClose(InputStream in) throws IOException { 387 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 388 byte[] buffer = new byte[1024]; 389 int count; 390 while ((count = in.read(buffer)) != -1) { 391 bytes.write(buffer, 0, count); 392 } 393 return bytes.toByteArray(); 394 } 395 396 public static void closeQuietly(AutoCloseable closeable) { 397 if (closeable != null) { 398 try { 399 closeable.close(); 400 } catch (RuntimeException rethrown) { 401 throw rethrown; 402 } catch (Exception ignored) { 403 } 404 } 405 } 406 } 407