Android Activity Result API with Hilt(Dependency Injection)

Hello folks, I wanted to tell you everything about Activity Result API, but I will not do at all. :D As far as you know there are a lot of writings and also you can reach official info from here.

I am going to tell you about Activity Result API with using DI (Hilt) and LifecycleObserver. At last days I have been using Hilt, I am giving my sample with it. We can exemplify with camera and storage permissions.

Step1:

Firstly we are creating two helper classes , the first one is;

interface PermissionListener {
fun permissionGranted()
fun permissionDenied()
}

The second one is;

abstract class AbstractPermissionObserver : LifecycleObserver {

var granted: Boolean = false
var permissionListener: PermissionListener? = null

abstract fun launch()

abstract fun checkPermission(): Boolean
}

AbstractPermissionObserve inherits from lifecycleObserver, that’s why we can bind this permission class to any activity lifecycle.

Step 2:

We had created AbstractLifecycleObserver in step 1, so we will write main injectable permission class which is inherited by AbstractPermissionObserver.

@ActivityScoped
class CameraPermissionObserver @Inject constructor(
@ActivityContext private val appCompatContext: Context
) : AbstractPermissionObserver() {

private var requestPermission: ActivityResultLauncher<Array<String>>? = null

@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate() {
if (appCompatContext is AppCompatActivity) {
requestPermission = appCompatContext.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.entries.forEach {
granted = it.value
if (!it.value) {
return@forEach
}
}
if (granted) {
permissionListener?.permissionGranted()
} else {
permissionListener?.permissionDenied()
}
}
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
requestPermission = null
}
override fun launch() {
requestPermission?.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE))
}

override fun checkPermission(): Boolean {
val cameraPermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.CAMERA)
val writePermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
val readPermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.READ_EXTERNAL_STORAGE)
return cameraPermission == PackageManager.PERMISSION_GRANTED &&
writePermission == PackageManager.PERMISSION_GRANTED &&
readPermission == PackageManager.PERMISSION_GRANTED
}
}

Above, I am defining ActivityScoped for this class because I want it will bind to activity lifecycle.Another reason is ActivityContext. I need to reach activity instance thanks to ActivityContext qualifier that has been predefined in Hilt, we can do it easily. Also we have two main function inside class, in onCreate method we are registering ActivityResult and in onDestroy we are equals requestPermission object to null to avoid memory leak issue.

Step3:

launch: We are using this method to trigger request permission dialog from user.

override fun launch() {
requestPermission?.launch(arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE))
}

Step4:

The last thing in CameraPermissionObserver class is checkPermission method. Sometimes we might need to check all permission granted or denied, we can learn permissions’ status with using this method implementation.

override fun checkPermission(): Boolean {
val cameraPermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.CAMERA)
val writePermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
val readPermission = ContextCompat.checkSelfPermission(appCompatContext, Manifest.permission.READ_EXTERNAL_STORAGE)
return cameraPermission == PackageManager.PERMISSION_GRANTED &&
writePermission == PackageManager.PERMISSION_GRANTED &&
readPermission == PackageManager.PERMISSION_GRANTED
}

So far, we have explained what the permission classes and therir functions are, as well as what they do. So now, how do we use it in activity?
Just remember, we defined CameraPermissionObserver class as ActivityScoped because of that we should add this observer to Any Activity class. Like below I have a BaseActivity class and I am injecting observer.

open class BaseActivity : AppCompatActivity() {

@Inject
lateinit var cameraPermissionObserver: CameraPermissionObserver

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTransparent()

lifecycle.addObserver(cameraPermissionObserver)
}
override fun onDestroy() {
super.onDestroy()
lifecycle.removeObserver(cameraPermissionObserver)
}
}

Eventually we can create a fragment to use our PermissionObserver.

@AndroidEntryPoint
class CameraPermissionSampleFragment : Fragment(R.layout.fragment_camera_permission_sample), PermissionListener {

@Inject
lateinit var cameraPermissionObserver: CameraPermissionObserver

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cameraPermissionObserver.permissionListener = this

btn_camera_permission_request.setOnClickListener {
requestPermission()
}
}

override fun onDestroyView() {
super.onDestroyView()
cameraPermissionObserver.permissionListener = null
}

override fun permissionGranted() {
Toast.makeText(requireActivity(), "Permission Granted", Toast.LENGTH_SHORT).show()
}

override fun permissionDenied() {
Toast.makeText(requireActivity(), "Permission Denied", Toast.LENGTH_SHORT).show()
}

private fun requestPermission() {
if (cameraPermissionObserver.checkPermission()){
// Go
} else {
cameraPermissionObserver.launch()
}
}
}

Github sample project is here.

If you have any issue for codebase, please warn me from comments, as soon as possible I will fix.

Happy coding :)

--

--

--

Oktein Company, Software Engineer, Python Enthusiast

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Android App Modularization — Quick Look

Implement Google SafetyNet ReCAPTCHA In Android Studio

Listening for incoming messages on the device with Flutter.

Meilleures applications Android : productivité et batterie

How to call a function from another widget | Flutter Tutorial

Fazenda da Grama

Google Map Platform 101 — Part 2: Map Style

Flutter: caching immutables with condition

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Oguzhan Alpayli

Oguzhan Alpayli

Oktein Company, Software Engineer, Python Enthusiast

More from Medium

Beginner: Integration of Huawei Push Notification with android Work Manager in Paralysis Glove IoT…

Basics for your first Android App: REST API, Database, and Fragment Navigation

Android : Basic Application with Kotlin ,MVVM ,Retrofit & Dagger Hilt

Auto Test and Sign Your App with GitHub Actions