I followed this tutorial to implement a new approach to request application permissions via Results API by RequestMultiplePermissions contract. Although the permission dialog is shown and permission result is propagated through the system to application preferences etc., my provided ActivityResultCallback is not notified at all.
Here are my source codes. I am aware I am not checking whether the user hasn't declined the permission already:
private fun checkPermissions() {
val permissionList = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION
)
val notGrantedPermissions = permissionList.map {
Pair(
it, ContextCompat.checkSelfPermission(
applicationContext,
it
)
)
}.filter {
it.second != PackageManager.PERMISSION_GRANTED
}
.map { it.first }
.toTypedArray()
if (notGrantedPermissions.isEmpty()) {
nextActivity()
} else {
requestPermissionLauncher.launch(notGrantedPermissions)
}
}
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
info("> requestPermissionLauncher - ${result.values}")
if (result.values.all { it }) {
nextActivity()
} else {
longToast("All permissions are required for app to work correctly")
checkPermissions()
}
}
Did I miss anything in the documentation?
Library version: androidx.activity:activity-ktx:1.2.0-alpha06
MinSdkVersion: 21
TargetSdkVersion: 29
I was up against this same issue tonight. I'm betting you're either extending AppCompatActivity or something similar. The issue with that is, when super.onRequestPermissionsResult is invoked, it falls to the FragmentActivity implementation which does not, itself, invoke the super method so the chain dies there. The quick solution is to extend ComponentActivity directly. However, if this is not feasible for your solution, you can override the method as follows:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
activityResultRegistry.dispatchResult(requestCode, Activity.RESULT_OK, Intent()
.putExtra(EXTRA_PERMISSIONS, permissions)
.putExtra(EXTRA_PERMISSION_GRANT_RESULTS, grantResults))
}
The above is a direct port from the ComponentActivity method.
Related
I have an activity that requires camera permission.
this activity can be called from several user configurable places in the app.
The rationale dialog and permission dialog themselves should be shown before the activity opens.
right now I am trying to handle these dialogs in some kind of extension function.
fun handlePermissions(context: Context, required_permissions: Array<String>, activity: FragmentActivity?, fragment: Fragment?): Boolean {
var isGranted = allPermissionsGranted(context, required_permissions)
if (!isGranted) {
//null here is where I used to pass my listener which was the calling fragment previously that implemented an interface
val dialog = DialogPermissionFragment(null, DialogPermissionFragment.PermissionType.QR)
activity?.supportFragmentManager?.let { dialog.show(it, "") }
//get result from dialog? how?
//if accepted launch actual permission request
fragment?.registerForActivityResult(ActivityResultContracts.RequestPermission()) { success ->
isGranted = success
}?.launch(android.Manifest.permission.CAMERA)
}
return isGranted
}
But I am having trouble to get the dialog results back from the rationale/explanation dialog.
My research until now brought me to maybe using a higher order function, to pass a function variable to the dialog fragment that returns a Boolean value to the helper function. But I am absolutely unsure if thats the right thing.
I thought using my own code would be cleaner and less overhead, could I achieve this easier when using a framework like eazy-permissions? (is Dexter still recommendable since its no longer under development)
is that even a viable thing I am trying to achieve here?
One approach that I've implemented and seems viable to use is this:
Class PermissionsHelper
class PermissionsHelper(private val activity: Activity) {
fun requestPermissionsFromDevice(
arrayPermissions: Array<String>, permissionsResultInterface: PermissionsResultInterface
) {
setPermissionResultInterface(permissionsResultInterface)
getMyPermissionRequestActivity().launch(arrayPermissions)
}
fun checkPermissionsFromDevice(permission: String): Boolean {
return ContextCompat.checkSelfPermission(activity, permission) ==
PackageManager.PERMISSION_GRANTED
}
}
Class PermissionsResultInterface to the helper class be able to communicate with the activity.
interface PermissionsResultInterface {
fun onPermissionFinishResult(permissions: MutableMap<String, Boolean>)
}
Usage with this approach to remove all files from app directory:
private fun clearFiles(firstCall: Boolean = false) {
if (verifyStoragePermissions(firstCall)) {
val dir = File(getExternalFilesDir(null).toString())
removeFileOrDirectory(dir)
Toast.makeText(
applicationContext,
resources.getString(R.string.done),
Toast.LENGTH_SHORT
).show()
}
}
private fun verifyStoragePermissions(firstCall: Boolean = false): Boolean {
val arrayListPermissions = arrayOf(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
)
for (permission in arrayListPermissions) {
if (!PermissionsHelper(this).checkPermissionsFromDevice(permission)) {
if (firstCall) PermissionsHelper(this)
.requestPermissionsFromDevice(arrayListPermissions, this)
else PermissionsDialogs(this).showPermissionsErrorDialog()
return false
}
}
return true
}
override fun onPermissionFinishResult(permissions: MutableMap<String, Boolean>) {
clearFiles()
}
With this approach you are able to call the permissions helper and using the result interface, after each of the answers from user, decide wether you still need to make a call for permissions or show a dialog to him.
If you need any help don't hesitate to contact me.
I am new to Android App development and I'd really like to know if there is a way to check in another Class (a Foreground Service that gathers some location data) if the location Permission was given in the Main Activity.
In my main Activity, I am requesting the permission straight upon app start like this:
private fun requestPermissions() {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSION_ID
)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_ID) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
Toast.makeText(this, "Right Permissions Granted", Toast.LENGTH_LONG).show()
}
}
}
}
Its working and I can give my app the permission to access the location. To use a function in my other I class, I need to check if the permission was granted, and I do it like this:
fun dummy(){
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
// do work that needs the location permission
}
}
However, if I try to execute this function, I get a null pointer reference. What am I missing here?
Thank you!
You can't request permissions from a Service. The reason is that when the request permission dialog comes up, the user is naturally going to think it belongs to the foreground app. THat confusion is why Google doesn't allow it. My best suggestion would be to launch an Activity that then asks for permission.
I want onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) method in fragment
I found google and anwser fragment
(mainActivity)getActivity.onrequestPermissionResult
but I don't know this parametor
Do you have any other way or know this parameter? All I know is requestCode
viewmodel
private val _permissionVisibility = MutableLiveData<Boolean>(ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PERMISSION_GRANTED)
val permissionVisibility: LiveData<Boolean>
get() = _permissionVisibility
xml binding
android:visibility="#{homeViewModel.permissionVisibility ? View.VISIBLE : View.GONE}"
mainActivity
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Timber.d("Test ${requestCode} , ${permissions} , $grantResults ")
}
You can use new Activity Result APIs to get the result of permission and activityresult in much simplier way.
The Activity Results API provides two methods — RequestPermission and RequestMultiplePermissions . These two does exactly what their names are. Here’s a quick example code.
// Requesting Location Permission
bi.btnRequestPermission.setOnClickListener {
askLocationPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
}
// Single Permission Contract
private val askLocationPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
if(result){
Log.e("TAG", "Location permnission granted")
}else{
Log.e("TAG", "Location permnission denied")
}
}
// -------------------------------------------------------------------
// Requesting Mutliple Permissions - Location & Bluetooth
bi.btnRequestPermission.setOnClickListener {
askMultiplePermissions(arrayOf(
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.BLUETOOTH
))
}
you have to add the following dependencies in your app’s build.gradle file.
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha04'
Check the blog for reference- https://wajahatkarim.com/2020/05/activity-results-api-onactivityresult/
I am quite new in android programming. I would like to ask about startActivityForResult() and ActivityCompat.requestPermissions() function and their design. I understand that result of those functions is handled by another Activity functions (onActivityResult() and onRequestPermissionsResult() respectively). But I don't understand why is it designed this way.
Especially with ActivityCompat.requestPermissions(). Why do I have to control if I have permission (ContextCompat.checkSelfPermission()), if I don't then ask for it (ActivityCompat.requestPermissions()). And then handle in completely different function if I got this permission or not?
I would expect somethink like:
askPermission(Context context, String permission, Runnable permissionGranted, Runnable permissionDenied)
which would call permissionGranted if I already have permission or if I got it from user. With this function I would have to care just if I have permission or I don't have it.
Now I have to distunguish if I have permission and then do synchronous task or I don't have it and then do "asynchronous" task in onRequestPermissionsResult() where I very often do the same, as I do if I already have permission.
My question is: Is there some reason, why are permissions designed this way? Is there some funtion as I wrote above to allow me just say what to do if I have and what to do if i don't have permission (in functional way)? Or is there some desing pattern to easy handle permissions and starting activities for result?
Thanks for your time and some explanation if you know why is this design good.
Definitely Not a good way!
If we use inheritence concept we may solve this problem a little
we can make it synchronous like this :
//Kotlin
askForPermissions(permissionList, onPermissionsGranted = {
//If permissions given
}, onPermissionFailed = {
//If permissions not given
})
buy using inheritence :
//Kotlin
open class PermissionActivity : AppCompatActivity() {
private val PERM_REQ_CODE = 1457
private lateinit var onPermissionsGranted: () -> Unit;
private lateinit var onPermissionFailed: () -> Unit;
private lateinit var perms: Array<String>
internal fun askForPermissions(perms: Array<String>, onPermissionsGranted: () -> Unit, onPermissionFailed: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkIfOneNotHasPermission(perms)) {
//Dont have permissions
this.perms = perms
this.onPermissionsGranted = onPermissionsGranted
this.onPermissionFailed = onPermissionFailed
requestPermissions(perms, PERM_REQ_CODE)
}
} else {
onPermissionsGranted.invoke()
}
}
#RequiresApi(Build.VERSION_CODES.M)
private fun checkIfOneNotHasPermission(perms: Array<String>): Boolean {
perms.forEach {
if (checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED) {
return true
}
}
return false
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
PERM_REQ_CODE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkIfOneNotHasPermission(perms)) {
onPermissionFailed.invoke()
} else {
onPermissionsGranted.invoke()
}
} else {
onPermissionsGranted.invoke()
}
}
else -> {
onPermissionFailed.invoke()
}
}
}
}
My widget makes calls to secure permissions outside of an Activity scope. Is it possible to request permissions for Android M outside of an Activity?
I found a workaround which seems to work fine. The trick is to create a transparent activity which is only there to request the permissions and is finished immediately afterwards. You'll still need a context of course but it doesn't have to be an activity.
The activity can return the result (granted or denied) via a broadcast (since startActivtyForResult is not possible outside of an activity).
You can use this activity:
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity
internal const val PERMISSIONS_KEY = "permissions"
internal const val ACTION_PERMISSIONS_GRANTED = "GetPermissionsActivity.permissions_granted"
internal const val ACTION_PERMISSIONS_DENIED = "GetPermissionsActivity.permissions_denied"
class GetPermissionsActivity: AppCompatActivity() {
private val permissionRequestCode = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityCompat.requestPermissions(
this,
intent.getStringArrayExtra(PERMISSIONS_KEY),
permissionRequestCode
)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == permissionRequestCode) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
sendBroadcast(Intent(ACTION_PERMISSIONS_GRANTED))
} else {
sendBroadcast(Intent(ACTION_PERMISSIONS_DENIED))
}
finish()
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}
And this style for the activity
<style name="Theme.Transparent" parent="Theme.AppCompat">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">#null</item>
</style>
In the manifest:
<activity android:name="GetPermissionsActivity" android:theme="#style/Theme.Transparent" />
And then use it like this (context required)
class SomeClass : BroadcastReceiver() {
private fun someFunction(context: Context) {
val intentFilter = IntentFilter()
intentFilter.addAction(ACTION_PERMISSIONS_GRANTED)
intentFilter.addAction(ACTION_PERMISSIONS_DENIED)
context.registerReceiver(this, intentFilter)
val intent = Intent(context, GetPermissionsActivity::class.java)
intent.putExtra(PERMISSIONS_KEY, arrayOf(<your permissions>))
context.startActivity(intent)
}
override fun onReceive(context: Context, intent: Intent) {
when {
intent.action == ACTION_PERMISSIONS_GRANTED -> {
context.unregisterReceiver(this)
onPermissionsGranted()
}
intent.action == ACTION_PERMISSIONS_DENIED -> {
context.unregisterReceiver(this)
onPermissionsDenied()
}
else -> super.onReceive(context, intent)
}
}
private fun onPermissionsGranted() {
// ...
}
private fun onPermissionsDenied() {
// ...
}
}
No, it's not possible. What you can do is to send a notification where the user can tap and then use an activity to request/manage the permission (maybe with dialog theme).
You could use the Easy Permissions library.
Android requires that these request come from an Activity. With Easy Permissions this is no longer an issue, you may request permission from anywhere as long as you provide Context. In addition, if you request a permission that is already granted the user will not be prompted.
Full disclosure, our company manages and develops this free to use library. That being said, we are confident that it is a useful tool and we would not share it otherwise.
You can only request permission either from Activity or from fragment.
Figure out a point in your Activity or Fragment where you feel App will require a permission, then call requestPermission method. sending notification will not work because you wanna your code processing un-till you get that requested permissions, and then resume your functionality from onRequestPermissionResult() method.
I think it's possible to request a permission outside of an Activity, as long as you use the method
ActivityCompat.requestPermissions (Activity activity, String[] permissions, int requestCode)
from the support library and pass the Activity as a parameter of the method.
For example:
ActivityCompat.requestPermissions(targetActivity, new String[] {Manifest.permission.CAMERA}, PERMISSION_REQUEST_CODE);
where targetActivity is the Activity that should implement the method:
onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)
That's the method that will handle de permission request result.
I was creating an app that required to check for permissions in many activities, so I created a static class that I could use globally in the app.
And it worked.
This worked for me.
I created a method to check request for permissions in a different class like this.
public class CheckForPermissions implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int MY_PERMISSIONS_REQUEST_READ_LOCATION = 1;
public static void checkForLocationPermissions(Activity context){
if(ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(context,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_READ_LOCATION);
}
}}
In my Activity, I called the method like this
CheckForPermissions.checkForLocationPermissions(this);