| page.title=Taking Photos Simply |
| parent.title=Capturing Photos |
| parent.link=index.html |
| |
| trainingnavtop=true |
| next.title=Recording Videos Simply |
| next.link=videobasics.html |
| |
| @jd:body |
| |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>This lesson teaches you to</h2> |
| <ol> |
| <li><a href="#TaskManifest">Request Camera Permission</a></li> |
| <li><a href="#TaskCaptureIntent">Take a Photo with the Camera App</a></li> |
| <li><a href="#TaskPhotoView">Get the Thumbnail</a></li> |
| <li><a href="#TaskPath">Save the Full-size Photo</a></li> |
| <li><a href="#TaskGallery">Add the Photo to a Gallery</a></li> |
| <li><a href="#TaskScalePhoto">Decode a Scaled Image</a></li> |
| </ol> |
| |
| <h2>You should also read</h2> |
| <ul> |
| <li><a href="{@docRoot}guide/topics/media/camera.html">Camera</a></li> |
| <li><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent |
| Filters</a></li> |
| </ul> |
| |
| <h2>Try it out</h2> |
| <div class="download-box"> |
| <a href="http://developer.android.com/shareables/training/PhotoIntentActivity.zip" |
| class="button">Download the |
| sample</a> |
| <p class="filename">PhotoIntentActivity.zip</p> |
| </div> |
| |
| </div> |
| </div> |
| |
| <p>This lesson explains how to capture photos using an existing camera |
| application.</p> |
| |
| <p>Suppose you are implementing a crowd-sourced weather service that makes a |
| global weather map by blending together pictures of the sky taken by devices |
| running your client app. Integrating photos is only a small part of your |
| application. You want to take photos with minimal fuss, not reinvent the |
| camera. Happily, most Android-powered devices already have at least one camera |
| application installed. In this lesson, you learn how to make it take a picture |
| for you.</p> |
| |
| |
| <h2 id="TaskManifest">Request Camera Permission</h2> |
| |
| <p>If an essential function of your application is taking pictures, then restrict |
| its visibility on Google Play to devices that have a camera. To advertise |
| that your application depends on having a camera, put a <a |
| href="{@docRoot}guide/topics/manifest/uses-feature-element.html"> {@code |
| <uses-feature>}</a> tag in your manifest file:</p> |
| |
| <pre style="clear:right"> |
| <manifest ... > |
| <uses-feature android:name="android.hardware.camera" |
| android:required="true" /> |
| ... |
| </manifest> |
| </pre> |
| |
| <p>If your application uses, but does not require a camera in order to function, instead set {@code |
| android:required} to {@code false}. In doing so, Google Play will allow devices without a |
| camera to download your application. It's then your responsibility to check for the availability |
| of the camera at runtime by calling {@link |
| android.content.pm.PackageManager#hasSystemFeature hasSystemFeature(PackageManager.FEATURE_CAMERA)}. |
| If a camera is not available, you should then disable your camera features.</p> |
| |
| |
| <h2 id="TaskCaptureIntent">Take a Photo with the Camera App</h2> |
| |
| <p>The Android way of delegating actions to other applications is to invoke an {@link |
| android.content.Intent} that describes what you want done. This process involves three pieces: The |
| {@link android.content.Intent} itself, a call to start the external {@link android.app.Activity}, |
| and some code to handle the image data when focus returns to your activity.</p> |
| |
| <p>Here's a function that invokes an intent to capture a photo.</p> |
| |
| <pre> |
| static final int REQUEST_IMAGE_CAPTURE = 1; |
| |
| private void dispatchTakePictureIntent() { |
| Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
| if (takePictureIntent.resolveActivity(getPackageManager()) != null) { |
| startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); |
| } |
| } |
| </pre> |
| |
| <p>Notice that the {@link android.app.Activity#startActivityForResult |
| startActivityForResult()} method is protected by a condition that calls |
| {@link android.content.Intent#resolveActivity resolveActivity()}, which returns the |
| first activity component that can handle the intent. Performing this check |
| is important because if you call {@link android.app.Activity#startActivityForResult |
| startActivityForResult()} using an intent that no app can handle, |
| your app will crash. So as long as the result is not null, it's safe to use the intent. </p> |
| |
| |
| |
| <h2 id="TaskPhotoView">Get the Thumbnail</h2> |
| |
| <p>If the simple feat of taking a photo is not the culmination of your app's |
| ambition, then you probably want to get the image back from the camera |
| application and do something with it.</p> |
| |
| <p>The Android Camera application encodes the photo in the return {@link android.content.Intent} |
| delivered to {@link android.app.Activity#onActivityResult onActivityResult()} as a small {@link |
| android.graphics.Bitmap} in the extras, under the key {@code "data"}. The following code retrieves |
| this image and displays it in an {@link android.widget.ImageView}.</p> |
| |
| <pre> |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { |
| Bundle extras = data.getExtras(); |
| Bitmap imageBitmap = (Bitmap) extras.get("data"); |
| mImageView.setImageBitmap(imageBitmap); |
| } |
| } |
| </pre> |
| |
| <p class="note"><strong>Note:</strong> This thumbnail image from {@code "data"} might be good for an |
| icon, but not a lot more. Dealing with a full-sized image takes a bit more |
| work.</p> |
| |
| |
| <h2 id="TaskPath">Save the Full-size Photo</h2> |
| |
| <p>The Android Camera application saves a full-size photo if you give it a file to |
| save into. You must provide a fully qualified file name where the camera app should |
| save the photo.</p> |
| |
| <p>Generally, any photos that the user captures with the device camera should be saved on |
| the device in the public external storage so they are accessible by all apps. |
| The proper directory for shared photos is provided by {@link |
| android.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()}, |
| with the {@link android.os.Environment#DIRECTORY_PICTURES} argument. Because the directory |
| provided by this method is shared among all apps, reading and writing to it requires the |
| {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and |
| {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, respectively. |
| The write permission implicitly allows reading, so if you need to write to the external |
| storage then you need to request only one permission:</p> |
| |
| <pre> |
| <manifest ...> |
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
| ... |
| </manifest> |
| </pre> |
| |
| <p>However, if you'd like the photos to remain private to your app only, you can instead use the |
| directory provided by {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}. |
| On Android 4.3 and lower, writing to this directory also requires the |
| {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission. Beginning with |
| Android 4.4, the permission is no longer required because the directory is not accessible |
| by other apps, so you can declare the permission should be requested only on the lower versions |
| of Android by adding the <a |
| href="{@docRoot}guide/topics/manifest/uses-permission-element.html#maxSdk">{@code maxSdkVersion}</a> |
| attribute:</p> |
| <pre> |
| <manifest ...> |
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" |
| android:maxSdkVersion="18" /> |
| ... |
| </manifest> |
| </pre> |
| |
| <p class="note"><strong>Note:</strong> Files you save in the directories provided by |
| {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} or |
| {@link android.content.Context#getFilesDir getFilesDir()} are deleted |
| when the user uninstalls your app.</p> |
| |
| <p>Once you decide the directory for the file, you need to create a |
| collision-resistant file name. You may wish also to save the path in a |
| member variable for later use. Here's an example solution in a method that returns |
| a unique file name for a new photo using a date-time stamp:</p> |
| |
| <pre> |
| String mCurrentPhotoPath; |
| |
| private File createImageFile() throws IOException { |
| // Create an image file name |
| String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); |
| String imageFileName = "JPEG_" + timeStamp + "_"; |
| File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); |
| File image = File.createTempFile( |
| imageFileName, /* prefix */ |
| ".jpg", /* suffix */ |
| storageDir /* directory */ |
| ); |
| |
| // Save a file: path for use with ACTION_VIEW intents |
| mCurrentPhotoPath = "file:" + image.getAbsolutePath(); |
| return image; |
| } |
| </pre> |
| |
| |
| <p>With this method available to create a file for the photo, you can now |
| create and invoke the {@link android.content.Intent} like this:</p> |
| |
| <pre> |
| static final int REQUEST_TAKE_PHOTO = 1; |
| |
| private void dispatchTakePictureIntent() { |
| Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); |
| // Ensure that there's a camera activity to handle the intent |
| if (takePictureIntent.resolveActivity(getPackageManager()) != null) { |
| // Create the File where the photo should go |
| File photoFile = null; |
| try { |
| photoFile = createImageFile(); |
| } catch (IOException ex) { |
| // Error occurred while creating the File |
| ... |
| } |
| // Continue only if the File was successfully created |
| if (photoFile != null) { |
| Uri photoURI = FileProvider.getUriForFile(this, |
| "com.example.android.fileprovider", |
| photoFile); |
| takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); |
| startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); |
| } |
| } |
| } |
| </pre> |
| |
| <p class="note"><strong>Note:</strong> We are using {@link |
| android.support.v4.content.FileProvider#getUriForFile} which returns a <code>content://</code> |
| URI. For more recent apps targeting Android N and higher, passing a <code>file://</code> URI |
| across a package boundary causes a {@link android.os.FileUriExposedException |
| FileUriExposedException}. |
| Therefore, we now present a more generic way of storing images using a |
| {@link android.support.v4.content.FileProvider FileProvider}. |
| </p> |
| |
| Now, you need to configure the {@link android.support.v4.content.FileProvider |
| FileProvider}. In your app's manifest, add a provider to your application: |
| |
| <pre> |
| <application> |
| ... |
| <provider |
| android:name="android.support.v4.content.FileProvider" |
| android:authorities="com.example.android.fileprovider" |
| android:exported="false" |
| android:grantUriPermissions="true"> |
| <meta-data |
| android:name="android.support.FILE_PROVIDER_PATHS" |
| android:resource="@xml/file_paths"></meta-data> |
| </provider> |
| ... |
| </application> |
| </pre> |
| |
| Make sure that the authorities string matches the second argument to {@link |
| android.support.v4.content.FileProvider#getUriForFile}. |
| In the meta-data section of the provider definition, you can see that |
| the provider expects eligible paths to be configured in a dedicated resource file, |
| <c>res/xml/file_paths.xml</c>. Here is the content required for this particular |
| example: |
| |
| <pre> |
| <?xml version="1.0" encoding="utf-8"?> |
| <paths xmlns:android="http://schemas.android.com/apk/res/android"> |
| <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" /> |
| </paths> |
| </pre> |
| |
| The path component corresponds to the path that is returned by |
| {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} |
| when called with {@link android.os.Environment#DIRECTORY_PICTURES |
| Environment.DIRECTORY_PICTURES}. Make sure that you replace |
| <code>com.example.package.name</code> with the actual package name of your |
| app. Also, checkout the documentation of {@link android.support.v4.content.FileProvider} for |
| an extensive description of path specifiers that you can use besides |
| <code>external-path</code>. |
| |
| <h2 id="TaskGallery">Add the Photo to a Gallery</h2> |
| |
| <p>When you create a photo through an intent, you should know where your image is located, because |
| you said where to save it in the first place. For everyone else, perhaps the easiest way to make |
| your photo accessible is to make it accessible from the system's Media Provider.</p> |
| |
| <p class="note"><strong>Note:</strong> If you saved your photo to the directory provided by |
| {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}, the media |
| scanner cannot access the files because they are private to your app.</p> |
| |
| <p>The following example method demonstrates how to invoke the system's media scanner to add your |
| photo to the Media Provider's database, making it available in the Android Gallery application |
| and to other apps.</p> |
| |
| <pre> |
| private void galleryAddPic() { |
| Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); |
| File f = new File(mCurrentPhotoPath); |
| Uri contentUri = Uri.fromFile(f); |
| mediaScanIntent.setData(contentUri); |
| this.sendBroadcast(mediaScanIntent); |
| } |
| </pre> |
| |
| |
| <h2 id="TaskScalePhoto">Decode a Scaled Image</h2> |
| |
| <p>Managing multiple full-sized images can be tricky with limited memory. If |
| you find your application running out of memory after displaying just a few |
| images, you can dramatically reduce the amount of dynamic heap used by |
| expanding the JPEG into a memory array that's already scaled to match the size |
| of the destination view. The following example method demonstrates this |
| technique.</p> |
| |
| <pre> |
| private void setPic() { |
| // Get the dimensions of the View |
| int targetW = mImageView.getWidth(); |
| int targetH = mImageView.getHeight(); |
| |
| // Get the dimensions of the bitmap |
| BitmapFactory.Options bmOptions = new BitmapFactory.Options(); |
| bmOptions.inJustDecodeBounds = true; |
| BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); |
| int photoW = bmOptions.outWidth; |
| int photoH = bmOptions.outHeight; |
| |
| // Determine how much to scale down the image |
| int scaleFactor = Math.min(photoW/targetW, photoH/targetH); |
| |
| // Decode the image file into a Bitmap sized to fill the View |
| bmOptions.inJustDecodeBounds = false; |
| bmOptions.inSampleSize = scaleFactor; |
| bmOptions.inPurgeable = true; |
| |
| Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); |
| mImageView.setImageBitmap(bitmap); |
| } |
| </pre> |
| |