1 page.title=Taking Photos Simply 2 parent.title=Capturing Photos 3 parent.link=index.html 4 5 trainingnavtop=true 6 next.title=Recording Videos Simply 7 next.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" 35 class="button">Download the 36 sample</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 44 application.</p> 45 46 <p>Suppose you are implementing a crowd-sourced weather service that makes a 47 global weather map by blending together pictures of the sky taken by devices 48 running your client app. Integrating photos is only a small part of your 49 application. You want to take photos with minimal fuss, not reinvent the 50 camera. Happily, most Android-powered devices already have at least one camera 51 application installed. In this lesson, you learn how to make it take a picture 52 for 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 58 its visibility on Google Play to devices that have a camera. To advertise 59 that your application depends on having a camera, put a <a 60 href="{@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 72 android:required} to {@code false}. In doing so, Google Play will allow devices without a 73 camera to download your application. It's then your responsibility to check for the availability 74 of the camera at runtime by calling {@link 75 android.content.pm.PackageManager#hasSystemFeature hasSystemFeature(PackageManager.FEATURE_CAMERA)}. 76 If 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 82 android.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}, 84 and 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> 89 static final int REQUEST_IMAGE_CAPTURE = 1; 90 91 private 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 100 startActivityForResult()} method is protected by a condition that calls 101 {@link android.content.Intent#resolveActivity resolveActivity()}, which returns the 102 first activity component that can handle the intent. Performing this check 103 is important because if you call {@link android.app.Activity#startActivityForResult 104 startActivityForResult()} using an intent that no app can handle, 105 your 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 112 ambition, then you probably want to get the image back from the camera 113 application and do something with it.</p> 114 115 <p>The Android Camera application encodes the photo in the return {@link android.content.Intent} 116 delivered to {@link android.app.Activity#onActivityResult onActivityResult()} as a small {@link 117 android.graphics.Bitmap} in the extras, under the key {@code "data"}. The following code retrieves 118 this image and displays it in an {@link android.widget.ImageView}.</p> 119 120 <pre> 121 @Override 122 protected 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 132 icon, but not a lot more. Dealing with a full-sized image takes a bit more 133 work.</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 139 save into. You must provide a fully qualified file name where the camera app should 140 save the photo.</p> 141 142 <p>Generally, any photos that the user captures with the device camera should be saved on 143 the device in the public external storage so they are accessible by all apps. 144 The proper directory for shared photos is provided by {@link 145 android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()}, 146 with the {@link android.os.Environment#DIRECTORY_PICTURES} argument. Because the directory 147 provided 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. 150 The write permission implicitly allows reading, so if you need to write to the external 151 storage 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 161 directory provided by {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}. 162 On Android 4.3 and lower, writing to this directory also requires the 163 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission. Beginning with 164 Android 4.4, the permission is no longer required because the directory is not accessible 165 by other apps, so you can declare the permission should be requested only on the lower versions 166 of Android by adding the <a 167 href="{@docRoot}guide/topics/manifest/uses-permission-element.html#maxSdk">{@code maxSdkVersion}</a> 168 attribute:</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 179 when the user uninstalls your app.</p> 180 181 <p>Once you decide the directory for the file, you need to create a 182 collision-resistant file name. You may wish also to save the path in a 183 member variable for later use. Here's an example solution in a method that returns 184 a unique file name for a new photo using a date-time stamp:</p> 185 186 <pre> 187 String mCurrentPhotoPath; 188 189 private 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 209 create and invoke the {@link android.content.Intent} like this:</p> 210 211 <pre> 212 static final int REQUEST_TAKE_PHOTO = 1; 213 214 private 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 240 you said where to save it in the first place. For everyone else, perhaps the easiest way to make 241 your 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 245 scanner 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 248 photo to the Media Provider's database, making it available in the Android Gallery application 249 and to other apps.</p> 250 251 <pre> 252 private 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 265 you find your application running out of memory after displaying just a few 266 images, you can dramatically reduce the amount of dynamic heap used by 267 expanding the JPEG into a memory array that's already scaled to match the size 268 of the destination view. The following example method demonstrates this 269 technique.</p> 270 271 <pre> 272 private 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