ActivityCompat onRequestPermissionsResult() not Called - android

I'm trying to write a small class to handle permission checking. Only problem is that onRequestPermissionsResult() never gets called:
class PermissionHelper(val activity: Activity) : ActivityCompat.OnRequestPermissionsResultCallback{
interface OnPermissionRequested {
fun onPermissionResponse(isPermissionGranted: Boolean)
}
private var listener: OnPermissionRequested? = null
fun isPermissionGranted(permission: String) : Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return activity.applicationContext.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
}
return true
}
fun requestPermission(permissions: Array<String>,
requestCode: Int,
listener: OnPermissionRequested) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.listener = listener
activity.requestPermissions(permissions, requestCode)
}
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray) {
val isPermissionGranted = grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
listener?.onPermissionResponse(isPermissionGranted)
}
companion object {
const val PERMISSIONS_REQUEST_READ_CALENDAR = 200
const val PERMISSIONS_REQUEST_WRITE_CALENDAR = 201
const val PERMISSIONS_REQUEST_CALENDAR = 101
const val PERMISSIONS_REQUEST_LOCATION = 102
const val READ_CALENDAR = Manifest.permission.READ_CALENDAR
const val WRITE_CALENDAR = Manifest.permission.WRITE_CALENDAR
const val ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION
}
}
The system dialog to request the permission pops up, but the response is never handled. (i.e. the break point inside onRequestPermissionsResult() is never hit). Nothing is null, and all values are correct.
Thanks for any help.

onRequestPermissionsResult will be called on your Activity. There is nothing in this code that is going to call the onRequestPermissionsResult of your PermissionHelper.
You need this in your activity
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray) {
helper.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

My suggestion is use this library for handle permissions:
https://github.com/Karumi/Dexter
If you don't want to use Dexter library, I suggest use ActivityCompat.requestPermissions() instead of activity.requestPermissions()
You can find more info in this answer:
https://stackoverflow.com/a/56761238/5661680

Related

Is there some way to override a function of superclass either from extension functions or from interfaces in kotlin?

In android to do some tasks we require the user to grant related permissions. Complete process of checking and requesting permission involves 3 steps: checking permission, requesting permission and checking result. If the permission is denied, then don't need to run that task. So to simplify this, I wrote the following interface and extension function in ActivityExtensions.kt
private val permissionTaskSource = HashMap<Int, TaskCompletionSource<Boolean>>()
interface PermissionHandler {
#RequiresApi(Build.VERSION_CODES.M)
fun Activity.permissionRequest(requestCode: Int, vararg permissions: String): Task<Boolean> {
requestPermissions(permissions, requestCode)
val tcs = TaskCompletionSource<Boolean>()
permissionTaskSource[requestCode] = tcs
return tcs.task
}
fun permissionResult(requestCode: Int, grantResults: IntArray) {
permissionTaskSource[requestCode]!!.setResult(grantResults.all { it == PERMISSION_GRANTED })
permissionTaskSource.remove(requestCode)
}
}
suspend fun <T> T.requiresPermission(vararg permissions: String): Unit? where T : Activity, T : PermissionHandler {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return Unit // If version lower than M then no need of runtime permissions.
if (permissions.all { ContextCompat.checkSelfPermission(this, it) == PERMISSION_GRANTED })
return Unit // If already all permissions granted, then no need to ask again.
val granted = permissionRequest(System.currentTimeMillis().toInt(), *permissions).await()
if (granted)
return Unit // Permission granted now
return null // Permission denied now
}
Whenever required some permissions to do a task in an activity or fragment, I can use this line:
private suspend fun someTask() {
requiresPermission(/* list of required permissions */) ?: run{ /*permission denied. handle properly*/ return }
// Inside fragment this has to be called with requiresActivity() receiver.
...
}
This whole thing is working fine.. but in every activity/fragment I have to write these 4 lines of code.
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
permissionResult(requestCode, grantResults)
}
Is there some way I can move these codes also to ActivityExtensions.kt file to make it much neater and avoid repetitive code?

How to move boilerplate code for permission from activity/fragment to other class

I have a lot of confusion with when can you send a context as a parameter and when can you not.
In most of our applications we require at least some runtime permissions, I want to move that code out of the Activity/fragment class and move to let's say a RuntimePermissionManager.
I don't want to move it to the BaseActivity or BaseFragment class, because requesting permissions and performing actions based on the result should have a single class following single responsibility principle.
Below is the code for requesting permissions:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
REQUEST_CODE_LOCATION_PERMISSION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Perform action
} else {
// Show description
}
}
}
}
private fun permissionCheck(permission: Array<String>, requestCode: Int, permissionGranted:() -> Unit) {
if(ContextCompat.checkSelfPermission(applicationContext,
permission[0]) == PackageManager.PERMISSION_GRANTED) {
permissionGranted()
} else {
ActivityCompat.requestPermissions(this,
permission,
requestCode)
}
}

Kotlin - Internet and location permissions problem

Ι am writing an android application that uses both location and internet connection permissions and I am running it on my tablet. As you can see, I have included both in the manifest file. The problem is that when OnConnected() function is called the below if in the MainActivity.kt evaluates to true and I can't proceed further in the function. So can you give me some hint to provide some more code if it is not relevant
AndroidManifest.xml :
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
MainActivity.kt
override fun onConnected(p0: Bundle?) {
if(ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
return;
}
//some code
}
You have to understand the logic behind permissions in Android. Just adding the permissions in the manifest, doesn't mean that the user has authorized the application to do what those permissions enable.
If your if condition evaluates to true, since those permissions are not granted, you have to ask the user for those permissions.
Android has a some good documentation on how to this.
I highly suggest you go over that.
From Android 6.0 onwards needs to request permission the best way is to do a permissionManager Class to Handle that and then check in your Activity if the user has accepted the permissions:
class PermissionManager private constructor() {
fun checkPermission(
activity: Activity,
permissionRequests: Array<String>?,
requestCode: Int
): Boolean {
permissionRequests?.let {
it.firstOrNull {
ContextCompat.checkSelfPermission(
activity,
it
) != PackageManager.PERMISSION_GRANTED
}?.apply {
ActivityCompat.requestPermissions(
activity,
permissionRequests,
requestCode
)
return false
}
}
return true
}
companion object {
val instance by lazy { PermissionManager() }
const val CAMERA_PERMISSION = 1
}
}
Then in your activity check the permission Result:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ar)
//If the user has the permission accepted the next times
if (PermissionManager.instance.checkPermission(
this, arrayOf(
Manifest.permission.CAMERA
), PermissionManager.CAMERA_PERMISSION
)
) {
initLogic()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
PermissionManager.CAMERA_PERMISSION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initLogic()
} else {
return
}
}
}
}
}

App not asking for permission upon installing

Why is this not working? I've modified the Manifest too.
It is not asking for permission as I've coded. It's behaving as if I'm not even requesting for any permission.
I've made a splash screen and I want it to ask for permission before it goes to the MainActivity.
Splash screen should last for 1sec.
class SplashActivity : AppCompatActivity() {
var permissionsString = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MODIFY_AUDIO_SETTINGS,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.PROCESS_OUTGOING_CALLS,
Manifest.permission.RECORD_AUDIO)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
if (hasPermissions(this#SplashActivity,*permissionsString)){
ActivityCompat.requestPermissions(this#SplashActivity,permissionsString,131)
}else{
Handler().postDelayed({
val startAct = Intent(this#SplashActivity,MainActivity::class.java)
startActivity(startAct)
this.finish()},1000)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when(requestCode){
131 ->{
if (grantResults.isNotEmpty()
&& grantResults[0]==PackageManager.PERMISSION_GRANTED
&& grantResults[1]==PackageManager.PERMISSION_GRANTED
&& grantResults[2]==PackageManager.PERMISSION_GRANTED
&& grantResults[3]==PackageManager.PERMISSION_GRANTED
&& grantResults[4]==PackageManager.PERMISSION_GRANTED){
Handler().postDelayed({
val startAct = Intent(this#SplashActivity,MainActivity::class.java)
startActivity(startAct)
this.finish()},1000)
}else{
Toast.makeText(this#SplashActivity,"Grant it Please",Toast.LENGTH_SHORT).show()
this.finish()
}
return
}
else->{
Toast.makeText(this#SplashActivity,"Something wrong",Toast.LENGTH_SHORT).show()
this.finish()
return
}
}
}
fun hasPermissions(context: Context,vararg permissions: String): Boolean{
var hasAllPermissions = true
for (permission in permissions){
val res = context.checkCallingOrSelfPermission(permission)
if (res!=PackageManager.PERMISSION_GRANTED){
hasAllPermissions = false
}
}
return hasAllPermissions
}
}
Thats because your if condition is wrong. It should on include NOT operator to ask for permission as below:
if (!hasPermissions(this#SplashActivity,*permissionsString)) {
//Ask for permission
}
Ask for permission if you don't have the permission. In your case, you are checking if you have permission, then ask for permission which is wrong.
Check your
targetSdkVersion
in project gradle file.
It should be >= 23 to call run-time permission, otherwise will take permission automatically.

Android callbacks and code re-usage

In a fragment, I have a downloading code. and I'm sure I need the download function in the other fragments too.
So I want to make it separate file as a library from the fragment, but the code contains some android callback methods which stacked on the Activity and I don't know how to handle it if I move it to another file (Class).
The download code in the fragment,
private fun beforeDownload() {
// check permission
val externalPermission = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (externalPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_ID_STORAGE_PERMISSION)
} else {
onDownload()
}
}
/** Android call-back method after requesting permission **/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
beforeDownload()
}
private fun onDownload() {
if (media >= 100000000) {
Toast.makeText(activity, "The media is over 100Mb", Toast.LENGTH_SHORT).show()
} else {
downloadMediaJob = launch(UI) { downloadMedia() }
}
}
// Android receiver when download completed
private val onDownloadComplete = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
Toast.makeText(activity, R.string.download_complete_msg, Toast.LENGTH_SHORT).show()
}
}
suspend private fun downloadMedia() {
downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
...
downloadedFileId = downloadManager.enqueue(request)
}
and the callback methods are
onRequestPermissionsResult
onDownloadComplete
How can I move them to MediaDownload class so that making it reusable?
Each Fragment needs to implement it's own lifecycle callback, but that call back can simply delegate to a method on an instance of an object.
For example in your code above:
private fun beforeDownload() {
// check permission
val externalPermission = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (externalPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_ID_STORAGE_PERMISSION)
} else {
onDownload()
}
}
/** Android call-back method after requesting permission **/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
beforeDownload()
}
You should just move the beforeDownload() method to some "model" class, create or inject an instance and then call the beforeDownload() method on that instance.
class SomeModel() {
fun beforeDownload() {
...
}
}
Each Fragment still needs the lifecycle method, but the main part of the code can be shared in the SomeModel class
override fun onRequestPermissionsResult(requestCode: Int, permissions:
Array<out String>, grantResults: IntArray) {
instanceOfSomeModel.beforeDownload()
}
The only way to complete remove the redundancy of having to implement even the minimal lifecycle method, would be to subclass the Fragment and add the call you your method in the override in the subclass, but you don't want to do that!

Categories

Resources