Home | History | Annotate | Download | only in camera
      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 &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
     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 &#64;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 &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
    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 &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
    180 when the user uninstalls your app.</p>
    181 
    182 <p>Once you decide the directory for the file, you need to create a
    183 collision-resistant file name. You may wish also to save the path in a
    184 member variable for later use. Here's an example solution in a method that returns
    185 a unique file name for a new photo using a date-time stamp:</p>
    186 
    187 <pre>
    188 String mCurrentPhotoPath;
    189 
    190 private 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
    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             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
    239 android.support.v4.content.FileProvider#getUriForFile} which returns a <code>content://</code>
    240 URI. For more recent apps targeting Android N and higher, passing a <code>file://</code> URI
    241 across a package boundary causes a {@link android.os.FileUriExposedException
    242 FileUriExposedException}.
    243 Therefore, we now present a more generic way of storing images using a
    244 {@link android.support.v4.content.FileProvider FileProvider}.
    245 </p>
    246 
    247 Now, you need to configure the {@link android.support.v4.content.FileProvider
    248 FileProvider}. 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 
    266 Make sure that the authorities string matches the second argument to {@link
    267 android.support.v4.content.FileProvider#getUriForFile}.
    268 In the meta-data section of the provider definition, you can see that
    269 the 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
    271 example:
    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 
    280 The path component corresponds to the path that is returned by
    281 {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}
    282 when called with {@link android.os.Environment#DIRECTORY_PICTURES
    283 Environment.DIRECTORY_PICTURES}. Make sure that you replace
    284 <code>com.example.package.name</code> with the actual package name of your
    285 app. Also, checkout the documentation of {@link android.support.v4.content.FileProvider} for
    286 an 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
    292 you said where to save it in the first place.  For everyone else, perhaps the easiest way to make
    293 your 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
    297 scanner 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
    300 photo to the Media Provider's database, making it available in the Android Gallery application
    301 and to other apps.</p>
    302 
    303 <pre>
    304 private 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
    317 you find your application running out of memory after displaying just a few
    318 images, you can dramatically reduce the amount of dynamic heap used by
    319 expanding the JPEG into a memory array that's already scaled to match the size
    320 of the destination view. The following example method demonstrates this
    321 technique.</p>
    322 
    323 <pre>
    324 private 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