In Android 11, when user select "deny" option for more than once for any permission request, then system will mark it as "Permanently denied".Once permanently denied, user has to enable in settings.From this time shouldShowRequestPermissionRationale() start's to return false
Three options are available for permission window , "Deny","Allow All time","Allow only this time". But in settings "Deny","Allow all the time","Ask every time" are present.
How to find when user selects "Ask me every time" from settings, because, checkSelfPermission() returns PackageManager.PERMISSION_DENIED,and shouldShowRequestPermissionRationale() return false. In this time I want to show permission window, instead of move to settings. Something similar to google map permission
Using the new ActivityResultsContract you can do this in the following manner
private val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result: MutableMap<String, Boolean> ->
val deniedList: List<String> = result.filter {
!it.value
}.map {
it.key
}
when {
deniedList.isNotEmpty() -> {
val map = deniedList.groupBy { permission ->
if (shouldShowRequestPermissionRationale(permission)) DENIED else EXPLAINED
}
map[DENIED]?.let {
// request denied , request again
}
map[EXPLAINED]?.let {
//request denied ,send to settings
}
}
else -> {
//All request are permitted
}
}
}
In OnCreate()[Make sure you ask permission in OnCreate , else application will crash] , ask the permission :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestPermissionLauncher.launch(REQUIRED_PERMISSIONS)
}
Ask requiredPermissions in the following manner :
private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE
)
Actually there is one more scenario:
if the user let you request the permission but don't chose any option and dismissed the dialog by tapping outside, the request finishes with denied and shouldShowRequestPermissionRationale() returns false.
That is the exact same behaviour as if the user selects don't ask again.
the permission where requested once, it results in denied and we should not show a explanation.
therefore we have to track if shouldShowRequestPermissionRationale() has returned true for once. if it switches back to false its denied permanent.
Use the following method in your activity (Camera permission is used in this example):
private fun requestPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
//Permission is denied
} else {
//ask permission
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CODE_CAMERA)
}
}
}
You only need to check the shouldShowRequestPermissionRationale() after user deny the permission
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
} else {
if(shouldShowRequestPermissionRationale(Manifest.permission.YOUR_RUNTIME_PERMISSION)){
//user hasn't denied permission permanently
}else{
//user has denied permanently,
//you should try to redirect user to settings
//to ask user grant it manually
}
}
}
requestPermissionLauncher.launch(Manifest.permission.YOUR_RUNTIME_PERMISSION)
Related
I've added this inside manifest:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
besides other permissions above <application
And i'm using the code provided in gogole docs:
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// FCM SDK (and your app) can post notifications.
} else {
// TODO: Inform user that that your app will not show notifications.
}
}
private fun askNotificationPermission() {
// This is only necessary for API level >= 33 (TIRAMISU)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
PackageManager.PERMISSION_GRANTED
) {
// FCM SDK (and your app) can post notifications.
} else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
// TODO: display an educational UI explaining to the user the features that will be enabled
// by them granting the POST_NOTIFICATION permission. This UI should provide the user
// "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
// If the user selects "No thanks," allow the user to continue without notifications.
} else {
// Directly ask for the permission
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
but POST_NOTIFICATIONS is red and when I do alt + enter it doesn't work
Make sure your compileSdk is 33 and if it's still not working do invalidate catches and rest
In Android 13, I need a basic flow to get permission for push notifications:
class MainActivity : ComponentActivity(), LocationListener {
val notificationPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// feature requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
}
}
private fun requestPushNotificationPermissions(){
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)) {
// granted
}else {
// not granted, ask for permission
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
This is what happened:
when user first installed the app, checkSelfPermission returns not granted, and we then lauch permission launcher
user sees the permission dialog in Android 13
user selects Allow
Expected: registerForActivityResult callback will be fired with isGranted true
Actual: registerForActivityResult callback is not fired.
Same if user selects Not Allow. callback is never fired. Why?
This is my dependencies:
implementation 'androidx.activity:activity-ktx:1.2.0-alpha07'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha07'
sadly can't help why it doesn't work. But I used EasyPermission for handling the permissons request and it works fine.
https://github.com/googlesamples/easypermissions
Turns out, the registerForActivityResult callback never fires, because somewhere in the Activity, there is this piece of old function "onRequestPermissionsResult" that is accidentally catching all permissions callback:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (requestCode == locationPermissionCode) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Location permission granted
}
else {
//Location permission denied
}
}else{
//Notification permission callback accidentally landed here silently
}
}
Hope this helps someone.
I am programming an app that connects to a device via Bluetooth, but every time I want to do something with the BluetoothDevice I have to insert a permission check like this (Kotlin):
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
42
)
}
Is there a workaround with one single permission check in the beginning of the app?
Thank you!
We have to check permission granted, otherwise it may crash your app.
but we can do in very handy way in Kotlin.
Follow below steps...
In your MainActivity or Very first activity, ask Bluetooth permission like below.
Create Permission Callback in Activity.
private val requestPermissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.all { it.value }) Toast.makeText(
this,
"Permission Granted",
Toast.LENGTH_SHORT
).show()
else Toast.makeText(this, "not accepted all the permissions", Toast.LENGTH_LONG).show()
}
Request a permission in onCreate method of Activity.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//your code
requestPermissionsLauncher.launch(
arrayOf(android.Manifest.permission.BLUETOOTH_CONNECT)
) //asking permission whatever we need to run app.
}
Create a kotlin Extension function to make sure to run only on Bluetooth permission is Granted.
fun <T> Context.runOnBluetoothPermission(block: () -> T) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
block()
} else {
Toast.makeText(
this,
"Bluetooth permission need to work this.",
Toast.LENGTH_SHORT
).show()
}
}
Use it extension function wherever you need.
example :
In SecondActivity.kt
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//ui functions
//apicalls if any
//functions that only run on Bluetooth permission
runOnBluetoothPermission{
getAllBluetoothDevices()
}
}
private fun getAllBluetoothDevices(){
//your code to get all bluetooth devices.
}
}
The user can revoke the permission at any time, by going to the app settings. When the permission is revoked, the activity will be recreated. This means you have to check at least once after onCreate and before using the permission, if you still have the permission.
TL;DR
No, your app might crash.
I tried to grant WRITE_SETTING permissions to allow my application to increase the brightness in a view I tried the code below as it is mentioned in the documentation of android but every time no popup that allows the user to accept or deny the permission I tried to debug the code grantResults [0] is always equal to -1
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_SETTINGS
) != PackageManager.PERMISSION_GRANTED
) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_SETTINGS)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_SETTINGS),
10
)
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
Settings.System.putInt(
this.contentResolver,
Settings.System.SCREEN_BRIGHTNESS, 255
)
}
}
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
10 -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
Settings.System.putInt(
this.contentResolver,
Settings.System.SCREEN_BRIGHTNESS,255
)
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return
}
// Add other 'when' lines to check for other
// permissions this app might request.
else -> {
// Ignore all other requests.
}
}
}
}
WRITE_SETTINGS is not one that you can request via requestPermissions(). That is only for dangerous permissions, and WRITE_SETTINGS does not have that protectionLevel.
Quoting the documentation:
Note: If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen. The app requests the user's approval by sending an intent with action Settings.ACTION_MANAGE_WRITE_SETTINGS. The app can check whether it has this authorization by calling Settings.System.canWrite().
why it still need permission checker when i have used EasyPermissions?
#SuppressLint("MissingPermission")
#AfterPermissionGranted(100)
private fun methodRequiresTwoPermission() {
val perms = Manifest.permission.ACCESS_FINE_LOCATION
if (EasyPermissions.hasPermissions(this, perms)) {
Toast.makeText(applicationContext, "Granted", Toast.LENGTH_SHORT).show()
mMap.isMyLocationEnabled = true // Call requires permission. #SuppressLint("MissingPermission")
mMap.getUiSettings().setMyLocationButtonEnabled(true)
} else {
Toast.makeText(applicationContext, "Denied", Toast.LENGTH_SHORT).show()
EasyPermissions.requestPermissions(
this, "Please allow the permission",
100, perms
)
}
}
The library you are using is not explicitly included in the Android SDK. It is a helper library developed by other developers by adding extra code.
Lint thinks that you are not requesting permissions at run-time because it doesn't see any code such as Activity#requestPermissions(), hence the warning.
But you can safely ignore or suppress the warning because it has been called from somewhere inside the library.