Code Monkey home page Code Monkey logo

quickpermissions-kotlin's Introduction

QuickPermissions-Kotlin Release

The most easiest way to handle Android Runtime Permissions in Kotlin.

Example

Inspiration

Android runtime permissions is pain.

Android runtime permissions was introduced in the Marshmallow (v 6.0) version of Android. It asks for permissions to user when they are running the app instead of asking for all the permissions while installing the app. It gives more control to users as they can give the permissions they want and deny to those who they do not fill comfortable with.

With this, it has increased the pain in the neck for the developer, to enable/disable features based on what permissions user has granted or denied. The model asking for permissions was design to function asynchronously, which increased the complexity of an app largely.

To make it with that, google has created it's own library easypermissions. (I didn't find it easy, but that's what they said). Still, you have to do handful of things and it's asynchronous way of handling things make it hard to manage. There are many other libraries as well to help developers easily handle it. But, all libraries has to manage it with proxy classes or managing and passing callbacks and all.

So, to solve this issue QuickPermissions was created. But, it turns out that, it doesn't work with instant-run and lots of developers can not afford to disable instant run, because it dramatically increased the build time. So, if you are working with Kotlin, you are lucky. This library is created to solve that problem in Kotlin. No gradle plugin and no long running build times. Asking for permissions synchronous way (It looks like that, but won't block the main thread, don't worry, no ANRs, promise!). And after the permissions are granted, it will continue executing the method block which was on hold. As simple as that.

Add it to your app

In your, project's build.gradle file:

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "http://jitpack.io/" }  // <-- THIS MUST BE ADDED
    }
}

As this library is using jitpack to publish this, you need to add jitpack url. If you already have, do not add that again.

In your app's build.gradle file, add the following dependency:

dependencies {
   implementation 'com.github.quickpermissions:quickpermissions-kotlin:0.4.0'
}

Now, read below for using directions.

How to do it?

Add this to your Activity extending AppCompatActivity or Fragment from the support library.

Let the library do all the hard stuff

Use runWithPermissions block, and you are all done. Library will manage everything, will ask for permissions, will show rationale dialog if denied and also ask to user to allow permissions from settings if user has permanently denied some permission(s) required by the method. You

fun methodWithPermissions() = runWithPermissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) {
    Toast.makeText(this, "Camera and audio recording permissions granted", Toast.LENGTH_SHORT).show();
    // Do the stuff with permissions safely
}

That's it! You are good to go.

Your inner code will not be executed until the permission mentioned will not be granted. If it already has the permissions, it will simply be executed right away. If not, it will ask for permissions and executes it after it was granted.

NOTE: The one thing is to make sure, your function should not return anything (return type should be Unit), as the asking permissions is asynchronous behavior, that can not be handled in a true synchronous way.

Advanced

You can optionally pass on the options object to that method, which can do some handful of things.

Things you can do with it:

  • handleRationale : true/false value indicating whether the library should handle rational dialog or not.
  • rationaleMessage : Custom rational message which will override default value if specified.
  • handlePermanentlyDenied : true/false value indicating whether the library should handle permissions permanently denied dialog or not.
  • permanentlyDeniedMessage : Custom permanently denied message which will override default value if specified.
  • rationaleMethod : Handle rational callback yourself. This will pass on the callback to this function if specified. This will provide you will QuickPermissionsRequest instance on which you can call, proceed() to continue asking permissions again, or call cancel() to cancel the flow.
  • permanentDeniedMethod : Handle permissions permanently denied callback yourself. This will pass on the callback to this function if specified. This will provide you will QuickPermissionsRequest instance on which you can call openAppSettings() on it to continue flow or cancel() to cancel the flow.
  • permissionsDeniedMethod : Callback method to handle cases when some/all permissions are denied at the end of asking permissions flow. This will provide you will QuickPermissionsRequest instance from which you can retrieve the permissions which were denied.
val quickPermissionsOption = QuickPermissionsOptions(
	handleRationale = false
    rationaleMessage = "Custom rational message",
    permanentlyDeniedMessage = "Custom permanently denied message",
    rationaleMethod = { req -> rationaleCallback(req) },
    permanentDeniedMethod = { req -> permissionsPermanentlyDenied(req) }
)

private fun methodRequiresPermissions() = runWithPermissions(Manifest.permission.WRITE_CALENDAR, Manifest.permission.RECORD_AUDIO, options = quickPermissionsOption) {
    Toast.makeText(this, "Cal and microphone permissions granted", Toast.LENGTH_LONG).show()
}

Summary

It's super simple and super easy. Just wrap your code with runWithPermissions block and you are good to go by avoiding all the complexity android runtime permissions introduces.

Sample

There is an sample app available in this repo. Just clone it and run app to check it out. Play with it and share your feedback.


Have any questions, or any trouble? Create an issue.

quickpermissions-kotlin's People

Contributors

juancruzgs avatar kirtan403 avatar maxkunes avatar sebastianengel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quickpermissions-kotlin's Issues

Positive Button & Negative Button all white color

Hi, i am using this library recently and i found that when i just using this from the readme

fun methodWithPermissions() = runWithPermissions(Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO) {
Toast.makeText(this, "Camera and audio recording permissions granted",
Toast.LENGTH_SHORT).show();
}

buttons all are white color text.

Screenshot as below:
https://imgur.com/a/f9Q7I6w

Migrate to AndroidX

It would be great if you migrate to AndroidX to make lib ready for the future and avoid possible conflicts.

Using quick permission in fragment

I'm trying to use this library in a fragment to get the current location using fused location provider. Here's the code. I just assume that it will work either in activity or fragment. But i get

" java.lang.IllegalStateException: FragmentManager is already executing transactions"

Maybe some more samples usecase code could help new users to use this library.

@SuppressLint("MissingPermission")
override fun onAttach(context: Context?) {
    super.onAttach(context)
    if (context != null) {
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)

        runWithPermissions(Manifest.permission.ACCESS_COARSE_LOCATION) {
            val locationClient = fusedLocationClient
            locationClient?.lastLocation?.addOnSuccessListener {
                // Got last known location. In some rare situations this can be null.
                Timber.d("last location $it")
                vm.currentLocation = it
            }
        }
    }
}

Dialog buttons too close to each other

Running this library on an emulator, Nexus 5x API 28.
When default permission denied/rationale dialog is showing, the buttons are too close to each other, please see the screenshot:
image
Maybe related to the Anko library?

Rationale dialog is hidden beneath permissions dialog when called from onResume

I'm trying to integrate this library into my app and I can't get it to work properly.

After first permissions dialog appears and user selects "deny", rationale dialog should pop up -- explaining why this permission is needed, am I right? I can't get it to work, rationale dialog indeed appears, but it's behind permissions dialog, which keeps appearing over and over again. Is this intended?

Callback is called if you call runWithPremissions twice

Hi there. I ran into an issue.

After revoking the permission from app settings screen I can't get to the system permission dialog anymore. The lib automatically calls callback function even though permission is not allowed.

Here is the code:

runWithPermissions(permission,
                        options = QuickPermissionsOptions(
                            handlePermanentlyDenied = true,
                            handleRationale = true,
                            permanentDeniedMethod = { denyFunction.invoke() },
                            permissionsDeniedMethod = { denyFunction.invoke() },
                            rationaleMethod = {
                                // We call this since on permission denied is not called
                                denyFunction.invoke()
                            }),
                        callback = { Timber.d("Permission callback") })

Fatal Exception: java.lang.IllegalStateException : No support from any classes other than AppCompatActivity/Fragment

Fatal Exception: java.lang.IllegalStateException: Found android.app.ContextImpl : No support from any classes other than AppCompatActivity/Fragment
at com.livinglifetechway.quickpermissions_kotlin.PermissionsManagerKt.runWithPermissionsHandler(PermissionsManager.kt:152)
at com.livinglifetechway.quickpermissions_kotlin.PermissionsManagerKt.runWithPermissions(PermissionsManager.kt:23)
at com.bluecrunch.base.permissions_checking.LocationPermissionManager.askForForegroundLocationPermission(LocationPermissionManager.kt:32)
at com.bluecrunch.base.permissions_checking.LocationPermissionManager.requestLocationPermissionFor(LocationPermissionManager.kt:22)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity.handlePermissionNotGranted(GoogleMapActivity.java:315)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity.handleLocationResult(GoogleMapActivity.java:286)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity.subscribeToLiveData$lambda-10(GoogleMapActivity.java:172)
at com.bluecrunch.base.BaseActivity$$InternalSyntheticLambda$0$ff4710b7aabc990dfac0223896b0686b79b5241f4721d0e59926a80db7746231$0.onChanged$bridge(BaseActivity.java:53)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapViewModel$getLocationPermissionStatus$1$1.invokeSuspend(GoogleMapViewModel.kt:44)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapViewModel$getLocationPermissionStatus$1$1.invoke(GoogleMapViewModel.kt:5)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapViewModel$getLocationPermissionStatus$1$1.invoke(GoogleMapViewModel.kt:5)
at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Collect.kt:136)
at kotlinx.coroutines.flow.FlowKt__ErrorsKt$catchImpl$$inlined$collect$1.emit(Collect.kt:136)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59)
at com.bluecrunch.microfinance.location_tracking.data.remote.LocationRemoteDataSource$getCurrentLocation$2.invokeSuspend(LocationRemoteDataSource.kt:54)
at com.bluecrunch.microfinance.location_tracking.data.remote.LocationRemoteDataSource$getCurrentLocation$2.invoke(LocationRemoteDataSource.kt:2)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(SafeFlow.java:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:212)
at kotlinx.coroutines.flow.FlowKt__ErrorsKt.catchImpl(FlowKt__ErrorsKt.java:230)
at kotlinx.coroutines.flow.FlowKt.catchImpl(FlowKt.java:1)
at kotlinx.coroutines.flow.FlowKt__ErrorsKt$catch$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:113)
at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1.collect(SafeCollector.common.kt:114)
at kotlinx.coroutines.flow.FlowKt__ReduceKt.firstOrNull(FlowKt__ReduceKt.java:252)
at kotlinx.coroutines.flow.FlowKt.firstOrNull(FlowKt.java:1)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapViewModel$getLocationPermissionStatus$1.invokeSuspend(GoogleMapViewModel.kt:45)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(CancellableKt.java:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(CancellableKt.java:25)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.java:110)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(BuildersKt__Builders_commonKt.java:56)
at kotlinx.coroutines.BuildersKt.launch(BuildersKt.java:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(BuildersKt__Builders_commonKt.java:47)
at kotlinx.coroutines.BuildersKt.launch$default(BuildersKt.java:1)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapViewModel.getLocationPermissionStatus(GoogleMapViewModel.java:36)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity.getLocation(GoogleMapActivity.kt:167)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity.initMapFragment$lambda-2(GoogleMapActivity.java:103)
at com.bluecrunch.microfinance.ui.maps.google.presentation.GoogleMapActivity$$InternalSyntheticLambda$0$7fb1e2ceeddf1f7420af5179f2f71a47bdd4f4f48ab083a7669329bb2f20d7c4$0.onMapReady(GoogleMapActivity.java:12)
at com.google.android.gms.maps.zzat.zzb(com.google.android.gms:play-services-maps@@18.1.0:1)
at com.google.android.gms.maps.internal.zzar.zza(com.google.android.gms:play-services-maps@@18.1.0:6)
at com.google.android.gms.internal.maps.zzb.onTransact(com.google.android.gms:play-services-maps@@18.1.0:3)
at android.os.Binder.transact(Binder.java:1085)
at ex.c(ex.java:2)
at com.google.maps.api.android.lib6.impl.bg.run(bg.java:1)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:233)
at android.os.Looper.loop(Looper.java:344)
at android.app.ActivityThread.main(ActivityThread.java:8204)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:589)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1071)

Mark this lib as deprecated

Please mark this project as deprecated because there is no maintenance for the library at all. For example: this library does not work for location permission handling for Android SDK 30 and higher.

Allow to customize the strings of the dialog buttons

I'm working in an app which supports Arabic language interface and I want to change the strings of the dialog action buttons that are displayed whenever a dialog is shown.

It's currently hard-coded in the com.livinglifetechway.quickpermissions_kotlin.util.PermissionCheckerFragment:

activity?.alert {
message = quickPermissionsRequest?.rationaleMessage.orEmpty()
positiveButton("TRY AGAIN") {
requestPermissionsFromUser()
}
negativeButton("CANCEL") {
clean()
}
}?.apply { isCancelable = false }?.show()

Suggestion : can we support the strings of negativeButton and positiveButton in the QuickPermissionsRequest?

Looking for maintainers

This has been quite a while I've been working on this. This library has helped a lot of people dealing with permissions very easy way. As I've been busy with the work and eventually moved to web, got no chance to update to make it compatible and make it up-to-date with latest release.

In my opinion, I feel this is the most clean way to deal with the permissions, I would like to invite someone who can maintain this library and update it to support new android sdk versions.

If anyone would like to maintain this and enhance features on this, I love to provide him/her the access. Please drop the message here or contact me on twitter "@kirtan403" with some mention and showing your past work.

Thanks in advance

java.lang.IllegalStateException when using new Hilt lib

After adding the new HIlt lib I had to remove this nice library because HIlt doesn't support support.Fragment but androidx Fragment only.
I tracked the problem to be when checking the Context in the Permission Manager class of this lib.
When arriving there the lib crash with the exception above and the message is that the Fragment is not attached.
Thanks

Does not go into settings

Method

     private fun permissionsPermanentlyDenied (req: QuickPermissionsRequest) {
         // this will be permanently
         // denied. Handle it your way.
         AlertDialog.Builder (this)
             .setTitle ("Permissions Denied")
             .setMessage (getString (R.string.permanentlyDeniedMessage_never))
             .setPositiveButton ("App Settings") {dialog, which -> req.openAppSettings ()}
             .setNegativeButton ("Cancel") {dialog, which -> req.cancel ()}
             .setCancelable (false)
             .show ()
     }

Causes a crash, does not go into the settings on the Google Pixel 2 Android Q emulator

And if set permission some of WRITE_SETTINGS - it's immediately calls this mehod permissionsPermanentlyDenied

Could not resolve org.jetbrains.anko:anko-commons:0.10.5

I'm not able to build my app after adding QuickPermission-Kotlin to my dependency

Its says:

Execution failed for task ':app:dataBindingMergeDependencyArtifactsDevDebug'.
> Could not resolve all files for configuration ':app:devDebugRuntimeClasspath'.
   > Could not find org.jetbrains.anko:anko-commons:0.10.5.
     Searched in the following locations:
       - https://dl.google.com/dl/android/maven2/org/jetbrains/anko/anko-commons/0.10.5/anko-commons-0.10.5.pom
       - https://repo.maven.apache.org/maven2/org/jetbrains/anko/anko-commons/0.10.5/anko-commons-0.10.5.pom
       - https://jitpack.io/org/jetbrains/anko/anko-commons/0.10.5/anko-commons-0.10.5.pom
     Required by:
         project :app > com.github.quickpermissions:quickpermissions-kotlin:0.4.1

Possible solution:
 - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html ```

permissionsDeniedMethod is never called

When handleRationale = false and handlePermanentlyDenied = false the method to handle denied permissions is never called.
When I deny the permission in the Android dialog, my callback is never called.

This is my code:

val options = QuickPermissionsOptions(
    handleRationale = false,
    handlePermanentlyDenied = false,
    permissionsDeniedMethod = { 
        // This method is never called
    }
)

activity.runWithPermissions(Manifest.permission.ACCESS_FINE_LOCATION, options = options) {
    // Works Ok
}

Option for handing OnPermissionDenied

I'm moving from gradle-plugin-based QuickPermissions to this one and I can't seem to find handler for @OnPermissionsDenied, but rather only @OnPermanentlyDenied.

While it's logical that when user denies the permission default QP rationale shows dialog that notifies about importance of requested permissions, user still can press cancel.

As I understand developers supposed to handle default, before request state as "permission denied" state, but I don't think that's a correct approach for every situation and thus QuickPermissionsOptions should contain handler for onPermissionDenied in addition to onPermissionPermanently denied.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.