1page.title=Taking Photos Simply 2parent.title=Capturing Photos 3parent.link=index.html 4 5trainingnavtop=true 6next.title=Recording Videos Simply 7next.link=videobasics.html 8 9@jd:body 10 11 12<div id="tb-wrapper"> 13 <div id="tb"> 14 15 <h2>This lesson teaches you to</h2> 16 <ol> 17 <li><a href="#TaskManifest">Request Camera Permission</a></li> 18 <li><a href="#TaskCaptureIntent">Take a Photo with the Camera App</a></li> 19 <li><a href="#TaskPhotoView">Get the Thumbnail</a></li> 20 <li><a href="#TaskPath">Save the Full-size Photo</a></li> 21 <li><a href="#TaskGallery">Add the Photo to a Gallery</a></li> 22 <li><a href="#TaskScalePhoto">Decode a Scaled Image</a></li> 23 </ol> 24 25 <h2>You should also read</h2> 26 <ul> 27 <li><a href="{@docRoot}guide/topics/media/camera.html">Camera</a></li> 28 <li><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent 29 Filters</a></li> 30 </ul> 31 32 <h2>Try it out</h2> 33 <div class="download-box"> 34 <a href="http://developer.android.com/shareables/training/PhotoIntentActivity.zip" 35class="button">Download the 36sample</a> 37 <p class="filename">PhotoIntentActivity.zip</p> 38 </div> 39 40 </div> 41</div> 42 43<p>This lesson explains how to capture photos using an existing camera 44application.</p> 45 46<p>Suppose you are implementing a crowd-sourced weather service that makes a 47global weather map by blending together pictures of the sky taken by devices 48running your client app. Integrating photos is only a small part of your 49application. You want to take photos with minimal fuss, not reinvent the 50camera. Happily, most Android-powered devices already have at least one camera 51application installed. In this lesson, you learn how to make it take a picture 52for you.</p> 53 54 55<h2 id="TaskManifest">Request Camera Permission</h2> 56 57<p>If an essential function of your application is taking pictures, then restrict 58its visibility on Google Play to devices that have a camera. To advertise 59that your application depends on having a camera, put a <a 60href="{@docRoot}guide/topics/manifest/uses-feature-element.html"> {@code 61<uses-feature>}</a> tag in your manifest file:</p> 62 63<pre style="clear:right"> 64<manifest ... > 65 <uses-feature android:name="android.hardware.camera" 66 android:required="true" /> 67 ... 68</manifest> 69</pre> 70 71<p>If your application uses, but does not require a camera in order to function, instead set {@code 72android:required} to {@code false}. In doing so, Google Play will allow devices without a 73camera to download your application. It's then your responsibility to check for the availability 74of the camera at runtime by calling {@link 75android.content.pm.PackageManager#hasSystemFeature hasSystemFeature(PackageManager.FEATURE_CAMERA)}. 76If a camera is not available, you should then disable your camera features.</p> 77 78 79<h2 id="TaskCaptureIntent">Take a Photo with the Camera App</h2> 80 81<p>The Android way of delegating actions to other applications is to invoke an {@link 82android.content.Intent} that describes what you want done. This process involves three pieces: The 83{@link android.content.Intent} itself, a call to start the external {@link android.app.Activity}, 84and some code to handle the image data when focus returns to your activity.</p> 85 86<p>Here's a function that invokes an intent to capture a photo.</p> 87 88<pre> 89static final int REQUEST_IMAGE_CAPTURE = 1; 90 91private void dispatchTakePictureIntent() { 92 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 93 if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 94 startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); 95 } 96} 97</pre> 98 99<p>Notice that the {@link android.app.Activity#startActivityForResult 100startActivityForResult()} method is protected by a condition that calls 101{@link android.content.Intent#resolveActivity resolveActivity()}, which returns the 102first activity component that can handle the intent. Performing this check 103is important because if you call {@link android.app.Activity#startActivityForResult 104startActivityForResult()} using an intent that no app can handle, 105your app will crash. So as long as the result is not null, it's safe to use the intent. </p> 106 107 108 109<h2 id="TaskPhotoView">Get the Thumbnail</h2> 110 111<p>If the simple feat of taking a photo is not the culmination of your app's 112ambition, then you probably want to get the image back from the camera 113application and do something with it.</p> 114 115<p>The Android Camera application encodes the photo in the return {@link android.content.Intent} 116delivered to {@link android.app.Activity#onActivityResult onActivityResult()} as a small {@link 117android.graphics.Bitmap} in the extras, under the key {@code "data"}. The following code retrieves 118this image and displays it in an {@link android.widget.ImageView}.</p> 119 120<pre> 121@Override 122protected void onActivityResult(int requestCode, int resultCode, Intent data) { 123 if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { 124 Bundle extras = data.getExtras(); 125 Bitmap imageBitmap = (Bitmap) extras.get("data"); 126 mImageView.setImageBitmap(imageBitmap); 127 } 128} 129</pre> 130 131<p class="note"><strong>Note:</strong> This thumbnail image from {@code "data"} might be good for an 132icon, but not a lot more. Dealing with a full-sized image takes a bit more 133work.</p> 134 135 136<h2 id="TaskPath">Save the Full-size Photo</h2> 137 138<p>The Android Camera application saves a full-size photo if you give it a file to 139save into. You must provide a fully qualified file name where the camera app should 140save the photo.</p> 141 142<p>Generally, any photos that the user captures with the device camera should be saved on 143the device in the public external storage so they are accessible by all apps. 144The proper directory for shared photos is provided by {@link 145android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()}, 146with the {@link android.os.Environment#DIRECTORY_PICTURES} argument. Because the directory 147provided by this method is shared among all apps, reading and writing to it requires the 148{@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and 149{@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, respectively. 150The write permission implicitly allows reading, so if you need to write to the external 151storage then you need to request only one permission:</p> 152 153<pre> 154<manifest ...> 155 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 156 ... 157</manifest> 158</pre> 159 160<p>However, if you'd like the photos to remain private to your app only, you can instead use the 161directory provided by {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}. 162On Android 4.3 and lower, writing to this directory also requires the 163{@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission. Beginning with 164Android 4.4, the permission is no longer required because the directory is not accessible 165by other apps, so you can declare the permission should be requested only on the lower versions 166of Android by adding the <a 167href="{@docRoot}guide/topics/manifest/uses-permission-element.html#maxSdk">{@code maxSdkVersion}</a> 168attribute:</p> 169<pre> 170<manifest ...> 171 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 172 android:maxSdkVersion="18" /> 173 ... 174</manifest> 175</pre> 176 177<p class="note"><strong>Note:</strong> Files you save in the directories provided by 178{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} or 179{@link android.content.Context#getFilesDir getFilesDir()} are deleted 180when the user uninstalls your app.</p> 181 182<p>Once you decide the directory for the file, you need to create a 183collision-resistant file name. You may wish also to save the path in a 184member variable for later use. Here's an example solution in a method that returns 185a unique file name for a new photo using a date-time stamp:</p> 186 187<pre> 188String mCurrentPhotoPath; 189 190private File createImageFile() throws IOException { 191 // Create an image file name 192 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 193 String imageFileName = "JPEG_" + timeStamp + "_"; 194 File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); 195 File image = File.createTempFile( 196 imageFileName, /* prefix */ 197 ".jpg", /* suffix */ 198 storageDir /* directory */ 199 ); 200 201 // Save a file: path for use with ACTION_VIEW intents 202 mCurrentPhotoPath = "file:" + image.getAbsolutePath(); 203 return image; 204} 205</pre> 206 207 208<p>With this method available to create a file for the photo, you can now 209create and invoke the {@link android.content.Intent} like this:</p> 210 211<pre> 212static final int REQUEST_TAKE_PHOTO = 1; 213 214private void dispatchTakePictureIntent() { 215 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 216 // Ensure that there's a camera activity to handle the intent 217 if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 218 // Create the File where the photo should go 219 File photoFile = null; 220 try { 221 photoFile = createImageFile(); 222 } catch (IOException ex) { 223 // Error occurred while creating the File 224 ... 225 } 226 // Continue only if the File was successfully created 227 if (photoFile != null) { 228 Uri photoURI = FileProvider.getUriForFile(this, 229 "com.example.android.fileprovider", 230 photoFile); 231 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); 232 startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); 233 } 234 } 235} 236</pre> 237 238<p class="note"><strong>Note:</strong> We are using {@link 239android.support.v4.content.FileProvider#getUriForFile} which returns a <code>content://</code> 240URI. For more recent apps targeting Android N and higher, passing a <code>file://</code> URI 241across a package boundary causes a {@link android.os.FileUriExposedException 242FileUriExposedException}. 243Therefore, we now present a more generic way of storing images using a 244{@link android.support.v4.content.FileProvider FileProvider}. 245</p> 246 247Now, you need to configure the {@link android.support.v4.content.FileProvider 248FileProvider}. In your app's manifest, add a provider to your application: 249 250<pre> 251<application> 252 ... 253 <provider 254 android:name="android.support.v4.content.FileProvider" 255 android:authorities="com.example.android.fileprovider" 256 android:exported="false" 257 android:grantUriPermissions="true"> 258 <meta-data 259 android:name="android.support.FILE_PROVIDER_PATHS" 260 android:resource="@xml/file_paths"></meta-data> 261 </provider> 262 ... 263</application> 264</pre> 265 266Make sure that the authorities string matches the second argument to {@link 267android.support.v4.content.FileProvider#getUriForFile}. 268In the meta-data section of the provider definition, you can see that 269the provider expects eligible paths to be configured in a dedicated resource file, 270<c>res/xml/file_paths.xml</c>. Here is the content required for this particular 271example: 272 273<pre> 274<?xml version="1.0" encoding="utf-8"?> 275<paths xmlns:android="http://schemas.android.com/apk/res/android"> 276 <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" /> 277</paths> 278</pre> 279 280The path component corresponds to the path that is returned by 281{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} 282when called with {@link android.os.Environment#DIRECTORY_PICTURES 283Environment.DIRECTORY_PICTURES}. Make sure that you replace 284<code>com.example.package.name</code> with the actual package name of your 285app. Also, checkout the documentation of {@link android.support.v4.content.FileProvider} for 286an extensive description of path specifiers that you can use besides 287<code>external-path</code>. 288 289<h2 id="TaskGallery">Add the Photo to a Gallery</h2> 290 291<p>When you create a photo through an intent, you should know where your image is located, because 292you said where to save it in the first place. For everyone else, perhaps the easiest way to make 293your photo accessible is to make it accessible from the system's Media Provider.</p> 294 295<p class="note"><strong>Note:</strong> If you saved your photo to the directory provided by 296{@link android.content.Context#getExternalFilesDir getExternalFilesDir()}, the media 297scanner cannot access the files because they are private to your app.</p> 298 299<p>The following example method demonstrates how to invoke the system's media scanner to add your 300photo to the Media Provider's database, making it available in the Android Gallery application 301and to other apps.</p> 302 303<pre> 304private void galleryAddPic() { 305 Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 306 File f = new File(mCurrentPhotoPath); 307 Uri contentUri = Uri.fromFile(f); 308 mediaScanIntent.setData(contentUri); 309 this.sendBroadcast(mediaScanIntent); 310} 311</pre> 312 313 314<h2 id="TaskScalePhoto">Decode a Scaled Image</h2> 315 316<p>Managing multiple full-sized images can be tricky with limited memory. If 317you find your application running out of memory after displaying just a few 318images, you can dramatically reduce the amount of dynamic heap used by 319expanding the JPEG into a memory array that's already scaled to match the size 320of the destination view. The following example method demonstrates this 321technique.</p> 322 323<pre> 324private void setPic() { 325 // Get the dimensions of the View 326 int targetW = mImageView.getWidth(); 327 int targetH = mImageView.getHeight(); 328 329 // Get the dimensions of the bitmap 330 BitmapFactory.Options bmOptions = new BitmapFactory.Options(); 331 bmOptions.inJustDecodeBounds = true; 332 BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); 333 int photoW = bmOptions.outWidth; 334 int photoH = bmOptions.outHeight; 335 336 // Determine how much to scale down the image 337 int scaleFactor = Math.min(photoW/targetW, photoH/targetH); 338 339 // Decode the image file into a Bitmap sized to fill the View 340 bmOptions.inJustDecodeBounds = false; 341 bmOptions.inSampleSize = scaleFactor; 342 bmOptions.inPurgeable = true; 343 344 Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); 345 mImageView.setImageBitmap(bitmap); 346} 347</pre> 348 349