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 &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
     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()} 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