Scott Main | 564e8aa | 2011-12-15 15:59:34 -0800 | [diff] [blame] | 1 | page.title=Taking Photos Simply |
Scott Main | 580f014 | 2011-12-15 16:47:26 -0800 | [diff] [blame] | 2 | parent.title=Capturing Photos |
Scott Main | 564e8aa | 2011-12-15 15:59:34 -0800 | [diff] [blame] | 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">View the Photo</a></li> |
| 20 | <li><a href="#TaskPath">Save the 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/topics/intents/intents-filters.html">Intents and Intent |
| 29 | Filters</a></li> |
| 30 | </ul> |
| 31 | |
| 32 | <h2>Try it out</h2> |
| 33 | <div class="download-box"> |
Scott Main | 580f014 | 2011-12-15 16:47:26 -0800 | [diff] [blame] | 34 | <a href="http://developer.android.com/shareables/training/PhotoIntentActivity.zip" |
| 35 | class="button">Download the |
Scott Main | 564e8aa | 2011-12-15 15:59:34 -0800 | [diff] [blame] | 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 in Android Market 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> |
| 64 | <manifest ... > |
| 65 | <uses-feature android:name="android.hardware.camera" /> |
| 66 | ... |
| 67 | </manifest ... > |
| 68 | </pre> |
| 69 | |
| 70 | <p>If your application uses, but does not require a camera in order to function, add {@code |
| 71 | android:required="false"} to the tag. In doing so, Android Market will allow devices without a |
| 72 | camera to download your application. It's then your responsibility to check for the availability |
| 73 | of the camera at runtime by calling {@link |
| 74 | android.content.pm.PackageManager#hasSystemFeature hasSystemFeature(PackageManager.FEATURE_CAMERA)}. |
| 75 | If a camera is not available, you should then disable your camera features.</p> |
| 76 | |
| 77 | |
| 78 | <h2 id="TaskCaptureIntent">Take a Photo with the Camera App</h2> |
| 79 | |
| 80 | <p>The Android way of delegating actions to other applications is to invoke an {@link |
| 81 | android.content.Intent} that describes what you want done. This process involves three pieces: The |
| 82 | {@link android.content.Intent} itself, a call to start the external {@link android.app.Activity}, |
| 83 | and some code to handle the image data when focus returns to your activity.</p> |
| 84 | |
| 85 | <p>Here's a function that invokes an intent to capture a photo.</p> |
| 86 | |
| 87 | <pre> |
| 88 | private void dispatchTakePictureIntent(int actionCode) { |
| 89 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
| 90 | startActivityForResult(takePictureIntent, actionCode); |
| 91 | } |
| 92 | </pre> |
| 93 | |
| 94 | |
| 95 | <p>Congratulations: with this code, your application has gained the ability to |
| 96 | make another camera application do its bidding! Of course, if no compatible |
| 97 | application is ready to catch the intent, then your app will fall down like a |
| 98 | botched stage dive. Here is a function to check whether an app can handle your intent:</p> |
| 99 | |
| 100 | <pre> |
| 101 | public static boolean isIntentAvailable(Context context, String action) { |
| 102 | final PackageManager packageManager = context.getPackageManager(); |
| 103 | final Intent intent = new Intent(action); |
| 104 | List<ResolveInfo> list = |
| 105 | packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); |
| 106 | return list.size() > 0; |
| 107 | } |
| 108 | </pre> |
| 109 | |
| 110 | |
| 111 | <h2 id="TaskPhotoView">View the Photo</h2> |
| 112 | |
| 113 | <p>If the simple feat of taking a photo is not the culmination of your app's |
| 114 | ambition, then you probably want to get the image back from the camera |
| 115 | application and do something with it.</p> |
| 116 | |
| 117 | <p>The Android Camera application encodes the photo in the return {@link android.content.Intent} |
| 118 | delivered to {@link android.app.Activity#onActivityResult onActivityResult()} as a small {@link |
| 119 | android.graphics.Bitmap} in the extras, under the key {@code "data"}. The following code retrieves |
| 120 | this image and displays it in an {@link android.widget.ImageView}.</p> |
| 121 | |
| 122 | <pre> |
| 123 | private void handleSmallCameraPhoto(Intent intent) { |
| 124 | Bundle extras = intent.getExtras(); |
| 125 | mImageBitmap = (Bitmap) extras.get("data"); |
| 126 | mImageView.setImageBitmap(mImageBitmap); |
| 127 | } |
| 128 | </pre> |
| 129 | |
| 130 | <p class="note"><strong>Note:</strong> This thumbnail image from {@code "data"} might be good for an |
| 131 | icon, but not a lot more. Dealing with a full-sized image takes a bit more |
| 132 | work.</p> |
| 133 | |
| 134 | |
| 135 | <h2 id="TaskPath">Save the Photo</h2> |
| 136 | |
| 137 | <p>The Android Camera application saves a full-size photo if you give it a file to |
| 138 | save into. You must provide a path that includes the storage volume, |
| 139 | folder, and file name.</p> |
| 140 | |
| 141 | <p>There is an easy way to get the path for photos, but it works only on Android 2.2 (API level 8) |
| 142 | and later:</p> |
| 143 | |
| 144 | <pre> |
| 145 | storageDir = new File( |
| 146 | Environment.getExternalStoragePublicDirectory( |
| 147 | Environment.DIRECTORY_PICTURES |
| 148 | ), |
| 149 | getAlbumName() |
| 150 | ); |
| 151 | </pre> |
| 152 | |
| 153 | <p>For earlier API levels, you have to provide the name of the photo |
| 154 | directory yourself.</p> |
| 155 | |
| 156 | <pre> |
| 157 | storageDir = new File ( |
| 158 | Environment.getExternalStorageDirectory() |
| 159 | + PICTURES_DIR |
| 160 | + getAlbumName() |
| 161 | ); |
| 162 | </pre> |
| 163 | |
| 164 | <p class="note"><strong>Note:</strong> The path component {@code PICTURES_DIR} is |
| 165 | just {@code Pictures/}, the standard location for shared photos on the external/shared |
| 166 | storage.</p> |
| 167 | |
| 168 | |
| 169 | <h3 id="TaskFileName">Set the file name</h3> |
| 170 | |
| 171 | <p>As shown in the previous section, the file location for an image should be |
| 172 | driven by the device environment. What you need to do yourself is choose a |
| 173 | collision-resistant file-naming scheme. You may wish also to save the path in a |
| 174 | member variable for later use. Here's an example solution:</p> |
| 175 | |
| 176 | <pre> |
| 177 | private File createImageFile() throws IOException { |
| 178 | // Create an image file name |
| 179 | String timeStamp = |
| 180 | new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); |
| 181 | String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_"; |
| 182 | File image = File.createTempFile( |
| 183 | imageFileName, |
| 184 | JPEG_FILE_SUFFIX, |
| 185 | getAlbumDir() |
| 186 | ); |
| 187 | mCurrentPhotoPath = image.getAbsolutePath(); |
| 188 | return image; |
| 189 | } |
| 190 | </pre> |
| 191 | |
| 192 | |
| 193 | <h3 id="TaskIntentFileName">Append the file name onto the Intent</h3> |
| 194 | |
| 195 | <p>Once you have a place to save your image, pass that location to the camera |
| 196 | application via the {@link android.content.Intent}.</p> |
| 197 | |
| 198 | <pre> |
| 199 | File f = createImageFile(); |
| 200 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f)); |
| 201 | </pre> |
| 202 | |
| 203 | |
| 204 | <h2 id="TaskGallery">Add the Photo to a Gallery</h2> |
| 205 | |
| 206 | <p>When you create a photo through an intent, you should know where your image is located, because |
| 207 | you said where to save it in the first place. For everyone else, perhaps the easiest way to make |
| 208 | your photo accessible is to make it accessible from the system's Media Provider.</p> |
| 209 | |
| 210 | <p>The following example method demonstrates how to invoke the system's media scanner to add your |
| 211 | photo to the Media Provider's database, making it available in the Android Gallery application |
| 212 | and to other apps.</p> |
| 213 | |
| 214 | <pre> |
| 215 | private void galleryAddPic() { |
| 216 | Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); |
| 217 | File f = new File(mCurrentPhotoPath); |
| 218 | Uri contentUri = Uri.fromFile(f); |
| 219 | mediaScanIntent.setData(contentUri); |
| 220 | this.sendBroadcast(mediaScanIntent); |
| 221 | } |
| 222 | </pre> |
| 223 | |
| 224 | |
| 225 | <h2 id="TaskScalePhoto">Decode a Scaled Image</h2> |
| 226 | |
| 227 | <p>Managing multiple full-sized images can be tricky with limited memory. If |
| 228 | you find your application running out of memory after displaying just a few |
| 229 | images, you can dramatically reduce the amount of dynamic heap used by |
| 230 | expanding the JPEG into a memory array that's already scaled to match the size |
| 231 | of the destination view. The following example method demonstrates this |
| 232 | technique.</p> |
| 233 | |
| 234 | <pre> |
| 235 | private void setPic() { |
| 236 | // Get the dimensions of the View |
| 237 | int targetW = mImageView.getWidth(); |
| 238 | int targetH = mImageView.getHeight(); |
| 239 | |
| 240 | // Get the dimensions of the bitmap |
| 241 | BitmapFactory.Options bmOptions = new BitmapFactory.Options(); |
| 242 | bmOptions.inJustDecodeBounds = true; |
| 243 | BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); |
| 244 | int photoW = bmOptions.outWidth; |
| 245 | int photoH = bmOptions.outHeight; |
| 246 | |
| 247 | // Determine how much to scale down the image |
| 248 | int scaleFactor = Math.min(photoW/targetW, photoH/targetH); |
| 249 | |
| 250 | // Decode the image file into a Bitmap sized to fill the View |
| 251 | bmOptions.inJustDecodeBounds = false; |
| 252 | bmOptions.inSampleSize = scaleFactor; |
| 253 | bmOptions.inPurgeable = true; |
| 254 | |
| 255 | Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); |
| 256 | mImageView.setImageBitmap(bitmap); |
| 257 | } |
| 258 | </pre> |
| 259 | |