How to Capture images via Camera or Pick them

The ability to share capture and share images is one of the qualities that have propelled smartphone usages over the past decade. Multi-billion dollar tech franchises like Instagram and Pinterest have thus been born. This ability tells us about ourselves as human beings, we love stories. However stories don’t just have to be put in words. We can also use images and series of images called videos to convey our story.

If you are a developer you need to incorporate the ability to capture images or select already captured images and import them into your application. Then you can do all types of interesting things to these images.

Fortunately for us android sdk provides classes and APIs we can use to provide this capability seamlessly into our application. So in this tutorial we learn how to capture images via camera and show them in an imageview. Alternatively, we will see how to select images from the gallery and render them on an imageview.

Example – Kotlin Android Capture From Camera or Select from ImagePicker

We are creating an app containing one activity. That activity has an imageview and two buttons below it. When the user clicks the first button, we will initiate the Camera via intent. Then we can capture and image and that image will be rendered on an imageview within our application. The second button allows us to open our system gallery. From the gallery we can select an image and have it rendered on our imageview.

Video Tutorial

Watch the video tutorial for more details and demo.

Step 1: Create our Project

Open app android studio and create a new project. Choose empty activity, choose Kotlin as our programming language. Make sure our app supports androidx artifacts. I will use Android API Level 15 as my minimum version.

Here is my app level build.gradle:

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "info.camposha.kimagepicker"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    testImplementation 'junit:junit:4.12'
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}

You can see there is no special dependency to be added.

Step 2 : Layouts

We will have just a single layout:

(a). activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:gravity="center|top"
              android:layout_height="match_parent">
    <TextView
            android:id="@+id/headerTxt"
            android:text="Camera and ImagePicker Tutorial"
            android:background="@color/colorAccent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:textAlignment="center"
            android:textStyle="bold"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:textColor="@android:color/white" />
    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:layout_marginTop="16dp"
            android:gravity="center"
            android:background="@android:color/darker_gray"
            android:orientation="vertical">

        <ImageView
                android:id="@+id/mImageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:contentDescription="Our Image"/>
    </LinearLayout>

    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        <Button
                android:layout_weight="0.5"
                android:layout_width="0dp"
                android:layout_height="45dp"
                android:padding="5dp"
                android:text="Capture"
                android:layout_marginTop="10dp"
                android:textColor="@android:color/white"
                android:background="@drawable/button_shape"
                android:id="@+id/btnCapture"/>

        <Button
                android:layout_weight="0.5"
                android:layout_width="0dp"
                android:layout_height="45dp"
                android:padding="5dp"
                android:text="Choose"
                android:layout_marginTop="10dp"
                android:textColor="@android:color/white"
                android:background="@drawable/button_shape"
                android:id="@+id/btnChoose"/>
    </LinearLayout>

</LinearLayout>

That Layout contains the following components:

  1. LinearLayout – To order our elements linearly either vertically or horizontally.
  2. TextView – To render text
  3. ImageView – To render images.
  4. Button – To allow us choose/select an image or capture one from the camera.

Classes

We have only one class.

(a). MainActivity.kt

Star by adding imports:

import android.annotation.TargetApi
import android.app.Activity
import android.content.ContentUris
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri

import android.os.Build
import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import java.io.File

Then make our class extend the AppCompatActivity:

class MainActivity : AppCompatActivity() {

Inside the class lets define our instance fields:

    //Our variables
    private var mImageView: ImageView? = null
    private var mUri: Uri? = null
    //Our widgets
    private lateinit var btnCapture: Button
    private lateinit var btnChoose : Button
    //Our constants
    private val OPERATION_CAPTURE_PHOTO = 1
    private val OPERATION_CHOOSE_PHOTO = 2

In the above, the imageview will render our image, the Uri is the uri of the image. btnCapture will allow us start our camera to capture the image. btnChoose will allow us open the gallery to choose image.

Let’s then create a method to initialize the above widgets:

    private fun initializeWidgets() {
        btnCapture = findViewById(R.id.btnCapture)
        btnChoose = findViewById(R.id.btnChoose)
        mImageView = findViewById(R.id.mImageView)
    }

Then a method to show our Toast message:

    private fun show(message: String) {
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
    }

Next we will create a method to capture our photo or image:

    private fun capturePhoto(){
        val capturedImage = File(externalCacheDir, "My_Captured_Photo.jpg")
        if(capturedImage.exists()) {
            capturedImage.delete()
        }
        capturedImage.createNewFile()
        mUri = if(Build.VERSION.SDK_INT >= 24){
            FileProvider.getUriForFile(this, "info.camposha.kimagepicker.fileprovider",
             capturedImage)
        } else {
            Uri.fromFile(capturedImage)
        }

        val intent = Intent("android.media.action.IMAGE_CAPTURE")
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri)
        startActivityForResult(intent, OPERATION_CAPTURE_PHOTO)
    }

In the above method you can see we have started by instantiating a File we are calling capturedImage. We have passed the location as well as the file name.

Then using the exists() method we have checked if a file with name as the captured image does exist. If so we delete.

Then we create a new file using the createNewFile() method. The we obtain the Uri for the file. Take note that if we are using a build sdk version of Android API Version 24 and above we use the FileProvider class, invoking its getUriForFile() method. Otherwise we use the good old fromFile() method of the Uri class.

Then we instantiate our intent and startActivityForResult.

Then to open our gallery and filter images:

    private fun openGallery(){
        val intent = Intent("android.intent.action.GET_CONTENT")
        intent.type = "image/*"
        startActivityForResult(intent, OPERATION_CHOOSE_PHOTO)
    }

We will be rendering our image as a bitmap:

    private fun renderImage(imagePath: String?){
        if (imagePath != null) {
            val bitmap = BitmapFactory.decodeFile(imagePath)
            mImageView?.setImageBitmap(bitmap)
        }
        else {
            show("ImagePath is null")
        }
    }

To obtain our image path we will use this method:

    private fun getImagePath(uri: Uri?, selection: String?): String {
        var path: String? = null
        val cursor = contentResolver.query(uri, null, selection, null, null )
        if (cursor != null){
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA))
            }
            cursor.close()
        }
        return path!!
    }

Here is how we will handle our image on Android API Level 19 and above:

    @TargetApi(19)
    private fun handleImageOnKitkat(data: Intent?) {
        var imagePath: String? = null
        val uri = data!!.data
        //DocumentsContract defines the contract between a documents provider and the platform.
        if (DocumentsContract.isDocumentUri(this, uri)){
            val docId = DocumentsContract.getDocumentId(uri)
            if ("com.android.providers.media.documents" == uri.authority){
                val id = docId.split(":")[1]
                val selsetion = MediaStore.Images.Media._ID + "=" + id
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                 selsetion)
            }
            else if ("com.android.providers.downloads.documents" == uri.authority){
                val contentUri = ContentUris.withAppendedId(Uri.parse(
                    "content://downloads/public_downloads"), java.lang.Long.valueOf(docId))
                imagePath = getImagePath(contentUri, null)
            }
        }
        else if ("content".equals(uri.scheme, ignoreCase = true)){
            imagePath = getImagePath(uri, null)
        }
        else if ("file".equals(uri.scheme, ignoreCase = true)){
            imagePath = uri.path
        }
        renderImage(imagePath)
    }

FULL Code

Here is the full code for MainActivity.kt:

package info.camposha.kimagepicker import android.annotation.TargetApi import android.app.Activity import android.content.ContentUris import android.content.Intent import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.DocumentsContract import android.provider.MediaStore import android.widget.Button import android.widget.ImageView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import java.io.File class MainActivity : AppCompatActivity() { //Our variables private var mImageView: ImageView? = null private var mUri: Uri? = null //Our widgets private lateinit var btnCapture: Button private lateinit var btnChoose : Button //Our constants private val OPERATION_CAPTURE_PHOTO = 1 private val OPERATION_CHOOSE_PHOTO = 2 private fun initializeWidgets() { btnCapture = findViewById(R.id.btnCapture) btnChoose = findViewById(R.id.btnChoose) mImageView = findViewById(R.id.mImageView) } private fun show(message: String) { Toast.makeText(this,message,Toast.LENGTH_SHORT).show() } private fun capturePhoto(){ val capturedImage = File(externalCacheDir, "My_Captured_Photo.jpg") if(capturedImage.exists()) { capturedImage.delete() } capturedImage.createNewFile() mUri = if(Build.VERSION.SDK_INT >= 24){ FileProvider.getUriForFile(this, "info.camposha.kimagepicker.fileprovider", capturedImage) } else { Uri.fromFile(capturedImage) } val intent = Intent("android.media.action.IMAGE_CAPTURE") intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri) startActivityForResult(intent, OPERATION_CAPTURE_PHOTO) } private fun openGallery(){ val intent = Intent("android.intent.action.GET_CONTENT") intent.type = "image/*" startActivityForResult(intent, OPERATION_CHOOSE_PHOTO) } private fun renderImage(imagePath: String?){ if (imagePath != null) { val bitmap = BitmapFactory.decodeFile(imagePath) mImageView?.setImageBitmap(bitmap) } else { show("ImagePath is null") } } private fun getImagePath(uri: Uri?, selection: String?): String { var path: String? = null val cursor = contentResolver.query(uri, null, selection, null, null ) if (cursor != null){ if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)) } cursor.close() } return path!! } @TargetApi(19) private fun handleImageOnKitkat(data: Intent?) { var imagePath: String? = null val uri = data!!.data //DocumentsContract defines the contract between a documents provider and the platform. if (DocumentsContract.isDocumentUri(this, uri)){ val docId = DocumentsContract.getDocumentId(uri) if ("com.android.providers.media.documents" == uri.authority){ val id = docId.split(":")[1] val selsetion = MediaStore.Images.Media._ID + "=" + id imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selsetion) } else if ("com.android.providers.downloads.documents" == uri.authority){ val contentUri = ContentUris.withAppendedId(Uri.parse( "content://downloads/public_downloads"), java.lang.Long.valueOf(docId)) imagePath = getImagePath(contentUri, null) } } else if ("content".equals(uri.scheme, ignoreCase = true)){ imagePath = getImagePath(uri, null) } else if ("file".equals(uri.scheme, ignoreCase = true)){ imagePath = uri.path } renderImage(imagePath) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String> , grantedResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantedResults) when(requestCode){ 1 -> if (grantedResults.isNotEmpty() && grantedResults.get(0) == PackageManager.PERMISSION_GRANTED){ openGallery() }else { show("Unfortunately You are Denied Permission to Perform this Operataion.") } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when(requestCode){ OPERATION_CAPTURE_PHOTO -> if (resultCode == Activity.RESULT_OK) { val bitmap = BitmapFactory.decodeStream( getContentResolver().openInputStream(mUri)) mImageView!!.setImageBitmap(bitmap) } OPERATION_CHOOSE_PHOTO -> if (resultCode == Activity.RESULT_OK) { if (Build.VERSION.SDK_INT >= 19) { handleImageOnKitkat(data) } } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initializeWidgets() btnCapture.setOnClickListener{capturePhoto()} btnChoose.setOnClickListener{ //check permission at runtime val checkSelfPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) if (checkSelfPermission != PackageManager.PERMISSION_GRANTED){ //Requests permissions to be granted to this application at runtime ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE), 1) } else{ openGallery() } } } } //end 

Resources

Here are downlaod and reference resources:

No. Site Link
1. Github Browse
2. Github Download
3. YouTube Watch

Now move to the next tutorial.

Best regards.