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&lt;uses-feature&gt;}</a> tag in your manifest file:</p>
62
63<pre style="clear:right">
64&lt;manifest ... >
65    &lt;uses-feature android:name="android.hardware.camera"
66                  android:required="true" /&gt;
67    ...
68&lt;/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&#64;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&lt;manifest ...>
155    &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
156    ...
157&lt;/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&lt;manifest ...>
171    &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
172                     android:maxSdkVersion="18" />
173    ...
174&lt;/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