Android Development
Camera Integration
Image Capture
Mobile App Development
Java Programming

Capture Image from Camera and Display in Activity

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Capturing an image from the camera and displaying it in an Android Activity involves launching the system camera app via an Intent, receiving the result, and loading the image into an ImageView. The modern approach uses the Activity Result API (registerForActivityResult) instead of the deprecated onActivityResult. For full-resolution images, you must provide a file URI via FileProvider. The thumbnail returned in the Intent extras is low resolution (around 160x120 pixels) and only suitable for previews.

Permissions

xml
1<!-- AndroidManifest.xml -->
2<uses-feature android:name="android.hardware.camera" android:required="false" />
3<uses-permission android:name="android.permission.CAMERA" />
4
5<!-- Required for saving full-size photos -->
6<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
7    android:maxSdkVersion="28" />
8
9<!-- FileProvider for sharing file URIs with the camera app -->
10<provider
11    android:name="androidx.core.content.FileProvider"
12    android:authorities="${applicationId}.fileprovider"
13    android:exported="false"
14    android:grantUriPermissions="true">
15    <meta-data
16        android:name="android.support.FILE_PROVIDER_PATHS"
17        android:resource="@xml/file_paths" />
18</provider>
xml
1<!-- res/xml/file_paths.xml -->
2<paths>
3    <external-files-path name="images" path="Pictures" />
4</paths>

FileProvider is required on Android 7+ (API 24) because direct file URIs (file://) throw FileUriExposedException. The provider shares files securely via content:// URIs.

Thumbnail Only (Quick Approach)

kotlin
1import android.graphics.Bitmap
2import android.os.Bundle
3import android.widget.ImageView
4import androidx.activity.result.contract.ActivityResultContracts
5import androidx.appcompat.app.AppCompatActivity
6
7class MainActivity : AppCompatActivity() {
8    private lateinit var imageView: ImageView
9
10    private val takePicture = registerForActivityResult(
11        ActivityResultContracts.TakePicturePreview()
12    ) { bitmap: Bitmap? ->
13        bitmap?.let { imageView.setImageBitmap(it) }
14    }
15
16    override fun onCreate(savedInstanceState: Bundle?) {
17        super.onCreate(savedInstanceState)
18        setContentView(R.layout.activity_main)
19        imageView = findViewById(R.id.imageView)
20
21        findViewById<android.widget.Button>(R.id.captureButton).setOnClickListener {
22            takePicture.launch(null)
23        }
24    }
25}

TakePicturePreview() returns a small Bitmap thumbnail. No file URI or storage permissions are needed. Use this only when image quality does not matter.

kotlin
1import android.net.Uri
2import android.os.Bundle
3import android.os.Environment
4import android.widget.ImageView
5import androidx.activity.result.contract.ActivityResultContracts
6import androidx.appcompat.app.AppCompatActivity
7import androidx.core.content.FileProvider
8import java.io.File
9import java.text.SimpleDateFormat
10import java.util.*
11
12class MainActivity : AppCompatActivity() {
13    private lateinit var imageView: ImageView
14    private lateinit var photoUri: Uri
15
16    private val takePicture = registerForActivityResult(
17        ActivityResultContracts.TakePicture()
18    ) { success: Boolean ->
19        if (success) {
20            imageView.setImageURI(photoUri)
21        }
22    }
23
24    override fun onCreate(savedInstanceState: Bundle?) {
25        super.onCreate(savedInstanceState)
26        setContentView(R.layout.activity_main)
27        imageView = findViewById(R.id.imageView)
28
29        findViewById<android.widget.Button>(R.id.captureButton).setOnClickListener {
30            photoUri = createImageUri()
31            takePicture.launch(photoUri)
32        }
33    }
34
35    private fun createImageUri(): Uri {
36        val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
37        val imageFile = File(
38            getExternalFilesDir(Environment.DIRECTORY_PICTURES),
39            "IMG_${timestamp}.jpg"
40        )
41        return FileProvider.getUriForFile(
42            this,
43            "${packageName}.fileprovider",
44            imageFile
45        )
46    }
47}

TakePicture() saves the full-resolution photo to the URI you provide. The camera app writes directly to this file, and you load it afterward.

Java Version

java
1import android.net.Uri;
2import android.os.Bundle;
3import android.os.Environment;
4import android.widget.Button;
5import android.widget.ImageView;
6import androidx.activity.result.ActivityResultLauncher;
7import androidx.activity.result.contract.ActivityResultContracts;
8import androidx.appcompat.app.AppCompatActivity;
9import androidx.core.content.FileProvider;
10import java.io.File;
11import java.text.SimpleDateFormat;
12import java.util.Date;
13import java.util.Locale;
14
15public class MainActivity extends AppCompatActivity {
16    private ImageView imageView;
17    private Uri photoUri;
18
19    private final ActivityResultLauncher<Uri> takePicture =
20        registerForActivityResult(new ActivityResultContracts.TakePicture(), success -> {
21            if (success) {
22                imageView.setImageURI(photoUri);
23            }
24        });
25
26    @Override
27    protected void onCreate(Bundle savedInstanceState) {
28        super.onCreate(savedInstanceState);
29        setContentView(R.layout.activity_main);
30        imageView = findViewById(R.id.imageView);
31
32        Button captureBtn = findViewById(R.id.captureButton);
33        captureBtn.setOnClickListener(v -> {
34            photoUri = createImageUri();
35            takePicture.launch(photoUri);
36        });
37    }
38
39    private Uri createImageUri() {
40        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
41        File imageFile = new File(
42            getExternalFilesDir(Environment.DIRECTORY_PICTURES),
43            "IMG_" + timestamp + ".jpg"
44        );
45        return FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", imageFile);
46    }
47}

Loading with Proper Orientation

kotlin
1import android.graphics.BitmapFactory
2import android.graphics.Matrix
3import androidx.exifinterface.media.ExifInterface
4
5fun loadBitmapWithRotation(uri: Uri): Bitmap? {
6    val inputStream = contentResolver.openInputStream(uri) ?: return null
7    val bitmap = BitmapFactory.decodeStream(inputStream)
8    inputStream.close()
9
10    // Read EXIF orientation
11    val exifStream = contentResolver.openInputStream(uri) ?: return bitmap
12    val exif = ExifInterface(exifStream)
13    val orientation = exif.getAttributeInt(
14        ExifInterface.TAG_ORIENTATION,
15        ExifInterface.ORIENTATION_NORMAL
16    )
17    exifStream.close()
18
19    val rotation = when (orientation) {
20        ExifInterface.ORIENTATION_ROTATE_90 -> 90f
21        ExifInterface.ORIENTATION_ROTATE_180 -> 180f
22        ExifInterface.ORIENTATION_ROTATE_270 -> 270f
23        else -> 0f
24    }
25
26    if (rotation == 0f) return bitmap
27
28    val matrix = Matrix().apply { postRotate(rotation) }
29    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
30}

Camera photos often have EXIF rotation metadata instead of actual pixel rotation. Without reading the EXIF data, images may appear rotated 90 degrees in the ImageView.

Using Glide for Efficient Loading

kotlin
1import com.bumptech.glide.Glide
2
3// Glide handles EXIF rotation, memory management, and scaling automatically
4Glide.with(this)
5    .load(photoUri)
6    .into(imageView)

Glide (or Coil/Picasso) handles image loading, EXIF rotation, memory caching, and downsampling automatically. For production apps, always use an image loading library instead of setImageURI().

Common Pitfalls

  • Using the thumbnail bitmap for anything beyond previews: The data.getExtras().get("data") bitmap from the old Intent API is approximately 160x120 pixels. Always use TakePicture() with a file URI for usable-quality photos.
  • FileUriExposedException on Android 7+: Passing a file:// URI to the camera Intent crashes on API 24+. Always use FileProvider.getUriForFile() to create a content:// URI.
  • Image appears rotated: Many cameras save photos in landscape orientation with EXIF rotation metadata. Use ExifInterface to read the orientation and rotate the bitmap, or use Glide which handles this automatically.
  • OutOfMemoryError with large images: Loading a full-resolution camera photo (12MP+) directly into a Bitmap consumes 48MB+ of memory. Use BitmapFactory.Options.inSampleSize to downsample, or use Glide which manages memory automatically.
  • Deprecated onActivityResult: The old startActivityForResult / onActivityResult pattern is deprecated. Use registerForActivityResult with ActivityResultContracts.TakePicture() for the modern, lifecycle-safe approach.

Summary

  • Use ActivityResultContracts.TakePicture() with a FileProvider URI for full-resolution photos
  • Use TakePicturePreview() only for quick thumbnails that do not need to be saved
  • Configure FileProvider in the manifest to avoid FileUriExposedException on Android 7+
  • Handle EXIF rotation with ExifInterface or use Glide for automatic orientation correction
  • Use image loading libraries (Glide, Coil) in production to handle memory, caching, and rotation
  • The Activity Result API replaces the deprecated onActivityResult pattern

Course illustration
Course illustration

All Rights Reserved.