2011-12-15 15:59:34 -08:00
|
|
|
page.title=Taking Photos Simply
|
2011-12-15 16:47:26 -08:00
|
|
|
parent.title=Capturing Photos
|
2011-12-15 15:59:34 -08:00
|
|
|
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>
|
2013-12-04 17:54:03 -08:00
|
|
|
<li><a href="#TaskPhotoView">Get the Thumbnail</a></li>
|
|
|
|
<li><a href="#TaskPath">Save the Full-size Photo</a></li>
|
2011-12-15 15:59:34 -08:00
|
|
|
<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>
|
2013-12-04 17:54:03 -08:00
|
|
|
<li><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent
|
2011-12-15 15:59:34 -08:00
|
|
|
Filters</a></li>
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
<h2>Try it out</h2>
|
|
|
|
<div class="download-box">
|
2011-12-15 16:47:26 -08:00
|
|
|
<a href="http://developer.android.com/shareables/training/PhotoIntentActivity.zip"
|
|
|
|
class="button">Download the
|
2011-12-15 15:59:34 -08:00
|
|
|
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
|
2012-01-27 17:56:49 -08:00
|
|
|
its visibility on Google Play to devices that have a camera. To advertise
|
2011-12-15 15:59:34 -08:00
|
|
|
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>
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<pre style="clear:right">
|
2011-12-15 15:59:34 -08:00
|
|
|
<manifest ... >
|
2013-12-04 17:54:03 -08:00
|
|
|
<uses-feature android:name="android.hardware.camera"
|
|
|
|
android:required="true" />
|
2011-12-15 15:59:34 -08:00
|
|
|
...
|
2013-12-04 17:54:03 -08:00
|
|
|
</manifest>
|
2011-12-15 15:59:34 -08:00
|
|
|
</pre>
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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
|
2011-12-15 15:59:34 -08:00
|
|
|
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>
|
|
|
|
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<h2 id="TaskCaptureIntent">Take a Photo with the Camera App</h2>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
<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>
|
2013-12-04 17:54:03 -08:00
|
|
|
static final int REQUEST_IMAGE_CAPTURE = 1;
|
|
|
|
|
|
|
|
private void dispatchTakePictureIntent() {
|
2011-12-15 15:59:34 -08:00
|
|
|
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
2013-12-04 17:54:03 -08:00
|
|
|
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
|
|
|
|
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
|
|
|
|
}
|
2011-12-15 15:59:34 -08:00
|
|
|
}
|
|
|
|
</pre>
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<h2 id="TaskPhotoView">Get the Thumbnail</h2>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
<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>
|
2013-12-04 17:54:03 -08:00
|
|
|
@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);
|
|
|
|
}
|
2011-12-15 15:59:34 -08:00
|
|
|
}
|
|
|
|
</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>
|
|
|
|
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<h2 id="TaskPath">Save the Full-size Photo</h2>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
<p>The Android Camera application saves a full-size photo if you give it a file to
|
2013-12-04 17:54:03 -08:00
|
|
|
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>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<pre>
|
|
|
|
<manifest ...>
|
|
|
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
|
|
...
|
|
|
|
</manifest>
|
|
|
|
</pre>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<p class="note"><strong>Note:</strong> Files you save in the directories provided by
|
|
|
|
{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} are deleted
|
|
|
|
when the user uninstalls your app.</p>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<pre>
|
|
|
|
String mCurrentPhotoPath;
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
private File createImageFile() throws IOException {
|
|
|
|
// Create an image file name
|
2013-12-04 17:54:03 -08:00
|
|
|
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
|
|
|
String imageFileName = "JPEG_" + timeStamp + "_";
|
|
|
|
File storageDir = Environment.getExternalStoragePublicDirectory(
|
|
|
|
Environment.DIRECTORY_PICTURES);
|
2011-12-15 15:59:34 -08:00
|
|
|
File image = File.createTempFile(
|
2013-12-04 17:54:03 -08:00
|
|
|
imageFileName, /* prefix */
|
|
|
|
".jpg", /* suffix */
|
|
|
|
storageDir /* directory */
|
2011-12-15 15:59:34 -08:00
|
|
|
);
|
2013-12-04 17:54:03 -08:00
|
|
|
|
|
|
|
// Save a file: path for use with ACTION_VIEW intents
|
|
|
|
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
|
2011-12-15 15:59:34 -08:00
|
|
|
return image;
|
|
|
|
}
|
2013-12-04 17:54:03 -08:00
|
|
|
</pre>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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>
|
2011-12-15 15:59:34 -08:00
|
|
|
|
|
|
|
<pre>
|
2014-01-21 10:12:07 -08:00
|
|
|
static final int REQUEST_TAKE_PHOTO = 1;
|
2013-12-04 17:54:03 -08:00
|
|
|
|
|
|
|
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) {
|
|
|
|
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
|
|
|
|
Uri.fromFile(photoFile));
|
|
|
|
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-12-15 15:59:34 -08:00
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
2013-12-04 17:54:03 -08:00
|
|
|
<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>
|
|
|
|
|
2011-12-15 15:59:34 -08:00
|
|
|
<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();
|
2013-12-04 17:54:03 -08:00
|
|
|
|
2011-12-15 15:59:34 -08:00
|
|
|
// 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;
|
2013-12-04 17:54:03 -08:00
|
|
|
|
2011-12-15 15:59:34 -08:00
|
|
|
// Determine how much to scale down the image
|
|
|
|
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
|
2013-12-04 17:54:03 -08:00
|
|
|
|
2011-12-15 15:59:34 -08:00
|
|
|
// Decode the image file into a Bitmap sized to fill the View
|
|
|
|
bmOptions.inJustDecodeBounds = false;
|
|
|
|
bmOptions.inSampleSize = scaleFactor;
|
|
|
|
bmOptions.inPurgeable = true;
|
2013-12-04 17:54:03 -08:00
|
|
|
|
2011-12-15 15:59:34 -08:00
|
|
|
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
|
|
|
|
mImageView.setImageBitmap(bitmap);
|
|
|
|
}
|
|
|
|
</pre>
|
|
|
|
|