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()} are deleted 179when the user uninstalls your app.</p> 180 181<p>Once you decide the directory for the file, you need to create a 182collision-resistant file name. You may wish also to save the path in a 183member variable for later use. Here's an example solution in a method that returns 184a unique file name for a new photo using a date-time stamp:</p> 185 186<pre> 187String mCurrentPhotoPath; 188 189private File createImageFile() throws IOException { 190 // Create an image file name 191 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 192 String imageFileName = "JPEG_" + timeStamp + "_"; 193 File storageDir = Environment.getExternalStoragePublicDirectory( 194 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 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, 229 Uri.fromFile(photoFile)); 230 startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); 231 } 232 } 233} 234</pre> 235 236 237<h2 id="TaskGallery">Add the Photo to a Gallery</h2> 238 239<p>When you create a photo through an intent, you should know where your image is located, because 240you said where to save it in the first place. For everyone else, perhaps the easiest way to make 241your photo accessible is to make it accessible from the system's Media Provider.</p> 242 243<p class="note"><strong>Note:</strong> If you saved your photo to the directory provided by 244{@link android.content.Context#getExternalFilesDir getExternalFilesDir()}, the media 245scanner cannot access the files because they are private to your app.</p> 246 247<p>The following example method demonstrates how to invoke the system's media scanner to add your 248photo to the Media Provider's database, making it available in the Android Gallery application 249and to other apps.</p> 250 251<pre> 252private void galleryAddPic() { 253 Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 254 File f = new File(mCurrentPhotoPath); 255 Uri contentUri = Uri.fromFile(f); 256 mediaScanIntent.setData(contentUri); 257 this.sendBroadcast(mediaScanIntent); 258} 259</pre> 260 261 262<h2 id="TaskScalePhoto">Decode a Scaled Image</h2> 263 264<p>Managing multiple full-sized images can be tricky with limited memory. If 265you find your application running out of memory after displaying just a few 266images, you can dramatically reduce the amount of dynamic heap used by 267expanding the JPEG into a memory array that's already scaled to match the size 268of the destination view. The following example method demonstrates this 269technique.</p> 270 271<pre> 272private void setPic() { 273 // Get the dimensions of the View 274 int targetW = mImageView.getWidth(); 275 int targetH = mImageView.getHeight(); 276 277 // Get the dimensions of the bitmap 278 BitmapFactory.Options bmOptions = new BitmapFactory.Options(); 279 bmOptions.inJustDecodeBounds = true; 280 BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); 281 int photoW = bmOptions.outWidth; 282 int photoH = bmOptions.outHeight; 283 284 // Determine how much to scale down the image 285 int scaleFactor = Math.min(photoW/targetW, photoH/targetH); 286 287 // Decode the image file into a Bitmap sized to fill the View 288 bmOptions.inJustDecodeBounds = false; 289 bmOptions.inSampleSize = scaleFactor; 290 bmOptions.inPurgeable = true; 291 292 Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); 293 mImageView.setImageBitmap(bitmap); 294} 295</pre> 296 297