Capturing photos with Android camera intent in your app
Android developers often need a quick way to let users take a picture without building a full camera module from scratch. The camera intent offers that — a bridge to the device's native camera app, returning the result straight to your activity. Whether you are prototyping a field tool in rural Queensland or shipping a social app for Sydney commuters, this pattern saves weeks of effort.
Australia's mobile-first culture makes camera integration especially relevant. A Melbourne cafe might want customers to photograph latte art, a Perth real estate startup may need property walkthroughs, and Darwin community groups often collect wildlife sightings. These use cases share a common requirement — a frictionless image capture experience that respects platform conventions.
The intent approach delegates capture to the system camera, so you benefit from vendor optimisations, hardware acceleration, and the familiar UI your users already trust. You also avoid fragile dependencies, since the system handles device variations transparently across phones sold at JB Hi-Fi, Harvey Norman, and through Telstra or Optus.
By the end of this walkthrough you will know how to declare permissions, dispatch the intent, retrieve the thumbnail or full-resolution file, and handle runtime quirks across the fragmented Android ecosystem.
Understanding the camera intent API
The Android intent system is a message-passing layer between components. When you want a photograph, you build an intent with the action MediaStore.ACTION_IMAGE_CAPTURE and ask the system to find an activity capable of fulfilling it. The result is a bundle containing either a small bitmap thumbnail or a URI pointing to a full-size file the camera wrote to disk.
This mechanism is part of a broader family of implicit intents that coordinate features between apps. If you have built a dropdown for choosing between camera or gallery sources, you already know this pattern, similar to how spinner styles for Android selection menus let you present options in a familiar list form.
There are two flavours worth knowing. ACTION_IMAGE_CAPTURE is the standard entry point and works on virtually every device, from a budget JB Hi-Fi handset to the latest flagship. ACTION_IMAGE_CAPTURE_SECURE is a quieter alternative that suppresses the shutter sound and excludes the image from the system gallery — useful for apps handling sensitive documents like a Sydney rideshare driver's licence check.
| Approach | Returns | File size | Best for |
|---|---|---|---|
ACTION_IMAGE_CAPTURE with thumbnail |
Bitmap extra | Small (~200 KB) | Quick previews, profile photos |
ACTION_IMAGE_CAPTURE with extra output URI |
File path | Full resolution | Document capture, printing |
ACTION_IMAGE_CAPTURE_SECURE |
Bitmap or URI | Variable | Sensitive images, silent capture |
| Custom Camera2 API | Full control | Full resolution | Pro photography, filters |
Declaring permissions and the FileProvider
Although firing the camera intent does not require the dangerous CAMERA permission, you do need to declare the camera feature in your manifest so Play Store filtering does not exclude devices without a camera. Add a <uses-feature> tag with android:name="android.hardware.camera", marking it required="false" if you want to ship to Android tablets without rear cameras, such as some kiosk units in Adelaide retail spaces.
For full-resolution captures, modern Android requires you to expose the file through a FileProvider. This content provider translates a private file path into a content URI the camera app can read. Without it, you will hit FileUriExposedException crashes on Android 7.0 and above.
Configure the FileProvider in your manifest with an authority matching your application ID, then create an XML file in res/xml/ describing which directories are shareable. The intent will write directly into your app's private storage, keeping the image safe from other apps and avoiding clutter in the user's gallery.
Dispatching the intent and handling the callback
Constructing the intent is straightforward. Create a new intent, set the action, and add either a thumbnail request via MediaStore.EXTRA_OUTPUT left null, or a full-resolution request by passing your FileProvider URI. Always wrap the start call in a try-catch for ActivityNotFoundException — older devices in regional Australia may not have a camera app, and your code should fail gracefully by suggesting an install or offering a text fallback.
Launch with startActivityForResult, supplying a request code you can recognise later. When capture finishes, the system returns control to your activity's onActivityResult method. Check the result code first — RESULT_OK means a photo was taken, while RESULT_CANCELED means the user backed out without shooting.
For thumbnail returns, the photo lives in the data extras under the "data" key as a bitmap. For full-resolution returns, the file is wherever your URI pointed, and you can decode it on demand using BitmapFactory with appropriate subsampling to avoid OutOfMemoryError on devices with constrained RAM — a real concern in the entry-level Telstra prepaid segment.
Decoding and displaying the captured image
Once you have the bitmap or file URI, decide how to render it. For a profile screen, setting an ImageView's source is enough. For a document scanner used by a Hobart conveyancing firm, you may want to run additional processing like edge detection or perspective correction before display.
Always decode bitmaps with inSampleSize to reduce memory pressure. A 12-megapixel photo decoded at full resolution consumes roughly 48 MB of RAM, which can crash an app on a two-year-old device. Calculate a sensible sample size based on the target view dimensions, then load the image in a background thread to keep the UI responsive.
If the user might retake the photo several times, show a preview immediately and offer a Retake button. This mirrors the system camera, which Australians are familiar with, and reduces friction — useful when documenting bright Brisbane sunlight or capturing receipts in a dimly lit Perth restaurant.
Common pitfalls on real devices
Several issues surface only after deployment. The first is rotation — if your activity is recreated between taking the photo and receiving the result, the URI or bitmap reference may become stale. Either disable rotation during capture or store the URI in onSaveInstanceState and restore it in onCreate.
The second pitfall is the FileProvider authority mismatch. If the authority in your manifest does not exactly match the one passed to getUriForFile, the camera app receives an invalid URI and either crashes or produces a blank file. A simple Log.d statement that prints the authority during development catches this quickly.
A third consideration is storage scope. On Android 10 and above, scoped storage changes how external directories behave, but because you are writing into private external storage via the FileProvider, this usually does not affect you. It matters, however, if you intend to keep the photo after the user uninstalls the app — in that case you must save it to shared media using MediaStore.
Practical recommendations for a reliable capture build
A dependable camera feature is rarely the product of a single clever snippet. It comes from layering a handful of well-trodden practices until the result feels invisible to the user.
- Test on at least three devices with different Android versions, including a low-end handset representative of the prepaid market sold through Telstra or Optus.
- Always wrap the intent dispatch in a try-catch and present a friendly fallback if no camera app is installed.
- Use the FileProvider pattern from the start, even for prototypes, so the code does not need rewriting when targeting modern Android.
- Offer a prominent Retake button after capture, especially for outdoor use cases like documenting surf at Bondi or wildlife in the Blue Mountains.
- Decode bitmaps off the main thread and use
inSampleSizeto keep memory usage within sensible bounds. - Log the FileProvider authority during development to catch mismatches before they reach production.
- Clean up temporary URIs when the user cancels capture, so leftover files do not accumulate in private storage.
When you treat the camera intent as a contract with the operating system rather than a casual API call, you build something that holds up across the long tail of devices Australians carry in their pockets every day.