Open In App

How to Build a Photo Viewing Application in Android using Jetpack Compose?

Last Updated : 19 Jul, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

Gallery App is one of the most used Applications which comes pre-installed on many Android devices and there are several different applications that are present in Google Play to view the media files present in a device. In this article, we will be building a simple Gallery Application for Android using Jetpack Compose. In this application, we will be displaying the photos present on the user’s device and the user will be able to open a single photo from the list of photos. 

Note: If you are seeking Java code for Jetpack Compose, please note that Jetpack Compose is only available in Kotlin. It uses features such as coroutines, and the handling of @Composable annotations is handled by a Kotlin compiler. There is no method for Java to access these. Therefore, you cannot use Jetpack Compose if your project does not support Kotlin.

Step by Step Implementation

Step 1: Create a New Project in Android Studio

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. While choosing the template, select Empty Compose Activity. If you do not find this template, try upgrading the Android Studio to the latest version. We demonstrated the application in Kotlin, so make sure you select Kotlin as the primary language while creating a New Project.

Step 2: Add the Dependency in build.gradle File

Navigate to the app > Gradle Scripts > build.gradle(:app) and add the below dependency to it. We are using Coil for loading images from paths in our Image view.

implementation("io.coil-kt:coil-compose:2.0.0-rc01") 

Now sync your project and we will move towards adding permissions in our AndroidManifest.xml file.

Step 3: Adding Permissions in our AndroidManifest.xml File

Navigate to the app > AndroidManifest.xml file and add the below permissions to it.

XML




<!-- permissions for reading external storage -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />


Step 4: Creating a New Activity for Displaying a Single Image

Navigate to the app > java > your app’s package name > Right-click on it > New >Compose> Empty Compose Activity and name your activity as MainActivity2 and create a new activity. We will be using this activity to display our single image from the list of different images. 

Step 5: Working with the MainActivity.kt File

Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




import android.Manifest.permission.READ_EXTERNAL_STORAGE
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.database.Cursor
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.Role.Companion.Image
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import coil.compose.rememberImagePainter
import com.example.newcanaryproject.ui.theme.*
import java.io.File
 
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NewCanaryProjectTheme {
                // on below line we are specifying
                // background color for our application
                Surface(
                    modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background
                ) {
                    // on below line we are specifying theme as scaffold.
                    Scaffold(
                        // in scaffold we are specifying top bar.
                        topBar = {
                            // inside top bar we are specifying background color.
                            TopAppBar(backgroundColor = greenColor,
                                // along with that we are specifying title for our top bar.
                                title = {
                                    // in the top bar we are specifying tile as a text
                                    Text(
                                        // on below line we are specifying text
                                        // to display in top app bar.
                                        text = "Gallery App",
                                        // on below line we are specifying modifier
                                        // to fill max width.
                                        modifier = Modifier.fillMaxWidth(),
                                        // on below line we are specifying text alignment.
                                        textAlign = TextAlign.Center,
                                        // on below line we are specifying color for our text.
                                        color = Color.White
                                    )
                                })
                        }) {
                        // on below line we are calling custom list
                        // view function to display custom listview.
                        com.example.newcanaryproject.requestPermissions(LocalContext.current, this)
                        gridView(LocalContext.current)
                    }
                }
            }
        }
    }
 
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // this method is called after permissions has been granted.
        when (requestCode) {
            101 ->
                // in this case we are checking if the permissions are accepted or not.
                if (grantResults.size > 0) {
                    val storageAccepted = grantResults[0] === PackageManager.PERMISSION_GRANTED
                    if (storageAccepted) {
                        // if the permissions are accepted we are displaying a toast message
                        // and calling a method to get image path.
                        Toast.makeText(this, "Permissions Granted..", Toast.LENGTH_SHORT).show()
                        getImagePath(this)
                    } else {
                        // if permissions are denied we are closing the app and displaying the toast message.
                        Toast.makeText(
                            this,
                            "Permissions denied, Permissions are required to use the app..",
                            Toast.LENGTH_SHORT
                        ).show()
                 }
            }
        }
    }
}
 
private fun checkPermission(ctx: Context): Boolean {
    // in this method we are checking if the permissions are granted or not and returning the result.
    val result = ContextCompat.checkSelfPermission(ctx, READ_EXTERNAL_STORAGE)
    return result == PackageManager.PERMISSION_GRANTED
}
 
private fun requestPermissions(ctx: Context, activity: Activity) {
    if (checkPermission(ctx)) {
        // if the permissions are already granted we are calling
        // a method to get all images from our external storage.
        Toast.makeText(ctx, "Permissions granted..", Toast.LENGTH_SHORT).show()
        getImagePath(ctx)
    } else {
        // if the permissions are not granted we are
        // requesting permissions on below line
        ActivityCompat.requestPermissions(
            activity,
            arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 101
        )
    }
}
 
private fun getImagePath(ctx: Context): MutableList<String> {
 
    var imgList: MutableList<String> = ArrayList()
    // in this method we are adding all our image paths
    // in our aerialist which we have created.
    // on below line we are checking if the device is having an sd card or not.
    val isSDPresent = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
    if (isSDPresent) {
        // if the sd card is present we are creating a new list in
        // which we are getting our images data with their ids.
        val columns = arrayOf(MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID)
        // on below line we are creating a new
        // string to order our images by string.
        val orderBy = MediaStore.Images.Media._ID
        // this method will stores all the images
        // from the gallery in Cursor
        val cursor: Cursor? = ctx.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            columns,
            null,
            null,
            orderBy
        )
        // below line is to get total number of images
        val count: Int = cursor!!.getCount()
        // on below line we are running a loop to add
        // the image file path in our array list.
        for (i in 0 until count) {
            // we will be only displaying 10 images
            // on below line we are breaking the loop
            // when array list is > 10
            if (imgList.size > 8) {
                break
            }
            // on below line we are moving our cursor position
            cursor!!.moveToPosition(i * 3)
            // on below line we are getting image file path
            val dataColumnIndex: Int = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
            // after that we are getting the image file path
            // and adding that path in our array list.
            imgList.add(cursor.getString(dataColumnIndex))
        }
        // after adding the data to our
        // array list we are closing our cursor.
        cursor!!.close()
    }
    // on below lien we are returning our list
    return imgList
}
 
// on below line we are creating grid view function for loading our grid view.
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)
@Composable
fun gridView(context: Context) {
    // on below line we are creating and initializing our array list
    var imgList: MutableList<String> = ArrayList()
    // on below line we are getting out list
    imgList = getImagePath(context)
    // on below line we are adding lazy
    // vertical grid for creating a grid view.
    LazyVerticalGrid(
        // on below line we are setting the
        // column count for our grid view.
        cells = GridCells.Fixed(3),
        // on below line we are adding padding
        // from all sides to our grid view.
        modifier = Modifier.padding(10.dp)
    ) {
        // on below line we are displaying our
        // items up to the size of the list.
        items(imgList.size) {
            // on below line we are creating a
            // card for each item of our grid view.
            Card(
                // inside our grid view on below line we are
                // adding on click for each item of our grid view.
                onClick = {
                    // on below line we are opening a new activity to display each activity.
                    val i = Intent(context, MainActivity2::class.java)
                    // on below line we are passing our image to new activity.
                    i.putExtra("img", imgList[it])
                    // on below line we are opening new activity
                    context.startActivity(i)
                },
                // on below line we are adding padding from our all sides.
                modifier = Modifier
                    .padding(3.dp)
                    .width(100.dp)
                    .height(100.dp),
 
                // on below line we are adding elevation for the card.
                elevation = 2.dp
            ) {
                // on below line we are creating a column on below sides.
                Column(
                    // on below line we are adding padding
                    // padding for our column and filling the max size.
                    Modifier
                        .fillMaxSize()
                        .fillMaxHeight()
                        .fillMaxWidth(),
                    // on below line we are adding
                    // horizontal alignment for our column
                    horizontalAlignment = Alignment.CenterHorizontally,
                    // on below line we are adding
                    // vertical arrangement for our column
                    verticalArrangement = Arrangement.Center
                ) {
                    // on below line we are creating an image file.
                    val imgFile = File(imgList[it])
                    // on below line we are creating image for our grid view item.
                    Image(
                        // on below line we are specifying the drawable image for our image.
                        //  painter = painterResource(id = courseList[it].languageImg),
                        painter = rememberImagePainter(data = imgFile),
                        // on below line we are specifying
                        // content description for our image
                        contentDescription = "Javascript",
                        // on below line we are setting height
                        // and width for our image.
                        modifier = Modifier
                            .fillMaxWidth()
                            .fillMaxHeight()
                    )
                }
            }
        }
    }
}


Step 6: Working with the MainActivity2.kt File

Go to the MainActivity2.kt file and refer to the following code. Below is the code for the MainActivity2.kt file. Comments are added inside the code to understand the code in more detail.

Kotlin




import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import coil.compose.rememberImagePainter
import com.example.newcanaryproject.ui.theme.NewCanaryProjectTheme
import com.example.newcanaryproject.ui.theme.greenColor
import java.io.File
 
class MainActivity2 : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        setContent {
            NewCanaryProjectTheme {
                // on below line we are specifying background color for our application
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    // on below line we are specifying theme as scaffold.
                    Scaffold(
                        // in scaffold we are specifying top bar.
                        topBar = {
                            // inside top bar we are specifying background color.
                            TopAppBar(backgroundColor = greenColor,
                                // along with that we are specifying title for our top bar.
                                title = {
                                    // in the top bar we are specifying tile as a text
                                    Text(
                                        // on below line we are specifying text to display in top app bar.
                                        text = "Gallery App",
                                        // on below line we are specifying modifier to fill max width.
                                        modifier = Modifier.fillMaxWidth(),
                                        // on below line we are specifying text alignment.
                                        textAlign = TextAlign.Center,
                                        // on below line we are specifying color for our text.
                                        color = Color.White
                                    )
                                }
                            )
                        }
                    ) {
                        var img = intent.getStringExtra("img")
                        // on below line we are calling get data and passing email and shared preferences to it.
                        displayImage(LocalContext.current, img)
                    }
                }
            }
        }
    }
}
 
@Composable
fun displayImage(ctx: Context, img: String?) {
    // on below line we are creating a column
    Column(
        // inside the column we are adding modifier and specifying max height, max width and max size.
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight()
            .fillMaxSize(),
        // on below line we are specifying vertical arrangement for our column.
        verticalArrangement = Arrangement.Center,
        // on below line we are specifying horizontal alignment for our column.
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val imgFile = File(img)
        // on below line we are creating image for our grid view item.
        Image(
            // on below line we are specifying the drawable image for our image.
            // painter = painterResource(id = courseList[it].languageImg),
            painter = rememberImagePainter(data = imgFile),
            // on below line we are specifying
            // content description for our image
            contentDescription = "Javascript",
            // on below line we are setting height
            // and width for our image.
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight()
        )
    }
}


Output:



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads