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&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()} 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&lt;application&gt;
252   ...
253   &lt;provider
254        android:name="android.support.v4.content.FileProvider"
255        android:authorities="com.example.android.fileprovider"
256        android:exported="false"
257        android:grantUriPermissions="true"&gt;
258        &lt;meta-data
259            android:name="android.support.FILE_PROVIDER_PATHS"
260            android:resource="@xml/file_paths"&gt;&lt;/meta-data&gt;
261    &lt;/provider&gt;
262    ...
263&lt;/application&gt;
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&lt;?xml version="1.0" encoding="utf-8"?&gt;
275&lt;paths xmlns:android="http://schemas.android.com/apk/res/android"&gt;
276    &lt;external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" /&gt;
277&lt;/paths&gt;
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