How do I create a single permission check in my android app? - android

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.

Related

Unable to read internal storage in Android Kotlin

I come to you in a time of great need. I am currently learning to use Kotlin for app development and as a "project" per-say, I am working on a simple "File manager". The current problem I am experiencing is that I am unable to read the directories and the files.
Using API 26
Using Kotlin
Using ViewModel
The permissions in the AndroidManifest.xml are set
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
The permission request in runtime is called in MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (checkPermission()) {
//permission allowed
val path = Environment.getExternalStorageDirectory().path
val filesAndFolders: Array<File>? = File(path).listFiles()
Log.d("FILETAG", path) // /storage/emulated/0
Log.d("FILETAG", filesAndFolders.toString()) // null
Log.d("FILETAG", File(path).exists().toString()) // true
Log.d("FILETAG", File(path).canRead().toString()) // false
} else {
//permission not allowed
requestPermission()
}
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
}
}
private fun checkPermission(): Boolean {
val result =
ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.READ_EXTERNAL_STORAGE
)
return result == PackageManager.PERMISSION_GRANTED
}
private fun requestPermission(){
if(ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)){
Toast.makeText(this, "Storage permission is required", Toast.LENGTH_SHORT).show()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
111
)
}
}
As commented in the code, the file array is returned as a "null", though the files seem to exist but are unreadable.
Additionally, I have tried executing this code from an inside of the fragment, but with the exact same results, though am required to read the files in a fragment rather than inside the MainActivity (But I first need to get this part of my code working before I move on to the fragments) and list the files in a RecyclerView.
This is my first question on Stackoverflow, if I missed any essential detail, let me know.
Please grant me your infinite knowledge, thank you.

How to detect permission's permanent deny in Android 11?

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)

Android - Permission Dialog not showing on button click

In my application, i need to request write and read permission from the storage. Since i want to show the user that the app needs these permissions, i have created and Activity containing a button, which on click, should call the Storage permission Dialog.
However, since the recent Android changes, this doesnt work anymore.
Is there a new (and clean) way to ask permission? Am i doing something wrong?
I have added the uses-permission line inside the AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
This is the code inside the Activity:
class ActivityPermission : AppCompatActivity() {
companion object {
var PERMISSION_REQUEST_CODE = 12
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPermissionBinding.inflate(layoutInflater)
setContentView(R.layout.activity_permission)
binding.btnPermission.setOnClickListener {
ActivityCompat.requestPermissions(this, arrayOf(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
PERMISSION_REQUEST_CODE)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, getString(R.string.permissiongranted), Toast.LENGTH_SHORT).show();
finish()
} else {
Toast.makeText(this, getString(R.string.permissiondenied), Toast.LENGTH_SHORT).show();
}
}
}
}
Thanks to everyone for helping me discover more about Android Permissions.
I decided to change the way i ask for the permission. Instead of an Activity, i decided to use a Fragment instead ("show an educational UI to the user. In this UI, describe why the feature, which the user wants to enable, needs a particular permission." - source | Thanks to #Michael in the comments for pointing it out).
Since now im using a Fragment, i have to use requestPermissions (Thanks to this reply). This now works flawlessly without any issues.
Turns out you need a combination of checks when trying to request a permission. You have to first check if the permission is actually enabled with checkSelfPermission, so you can easily choose where the user should go to start using the app.

Approach to reduce writing the same boring code when working with permissions

Currently, I am writing a chat application for Android. Starting from 23 SDK and above, it needs some permissions which user has to allow, such as extreme important (my chat will use a location of creation of a particular chat) and just small features such as uploading images to Firebase storage (it needs the access to phone storage, therefore it needs appropriate permission).
I have the following interface for callbacks.
object PermissionUtils {
interface PermissionAskListener {
fun onPermissionGranted()
/*
User has already granted this permission
The app must had been launched earlier and the user must had "allowed" that permission
*/
fun onPermissionRequest()
/*
The app is launched FIRST TIME..
We don't need to show additional dialog, we just request for the permission..
*/
fun onPermissionPreviouslyDenied()
/*
The app was launched earlier and the user simply "denied" the permission..
The user had NOT clicked "DO NOT SHOW AGAIN"
We need to show additional dialog in this case explaining how "allowing this permission" would be useful to the user
*/
fun onPermissionDisabled()
/*
The app had launched earlier and the user "denied" the permission..
AND ALSO had clicked "DO NOT ASK AGAIN"
We need to show Toask/alertdialog/.. to indicate that the user had denied the permission by checking do not disturb too...
So, you might want to take the user to setting>app>permission page where the user can allow the permission..
*/
}
fun checkForPermission(activity: Activity, permission: String, permissionAskListener: PermissionAskListener) {
//code omitted, here's the logic of calls listener members
}
}
And, I use it in code like this:
//calling from onCreate()
checkForPermission(
this, android.Manifest.permission.READ_EXTERNAL_STORAGE,
object : PermissionAskListener {
override fun onPermissionGranted() {
showToast(getString(R.string.msg_permissions_granted), Toast.LENGTH_LONG)
uplodAnImageToFirebase()
}
override fun onPermissionRequest() {
ActivityCompat.requestPermissions(
this#MainActivity, arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), readStorage
)
}
override fun onPermissionPreviouslyDenied() {
AlertDialog.Builder(this#MainActivity)
.setTitle(getString(R.string.title_permission_required))
.setMessage(getString(R.string.msg_permission_required))
.setCancelable(false)
.setPositiveButton(getString(R.string.action_allow)) { _, _ ->
ActivityCompat.requestPermissions(
this#MainActivity,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
readStorage
)
}
.setNegativeButton(getString(R.string.action_cancel)) { dialog, _ ->
dialog.cancel()
showToast(getString(R.string.msg_we_cant_give_functionality), Toast.LENGTH_LONG)
}
.show()
}
override fun onPermissionDisabled() {
AlertDialog.Builder(this#MainActivity)
.setTitle(getString(R.string.title_permission_disabled))
.setMessage(getString(R.string.msg_please_enable_permission))
.setCancelable(false)
.setPositiveButton(
getString(R.string.action_go_to_settings)
) { _, _ -> startActivity(Intent(Settings.ACTION_SETTINGS)) }
.setNegativeButton(getString(R.string.action_cancel)) { dialog, _ ->
dialog.cancel()
showToast(getString(R.string.msg_we_cant_give_functionality), Toast.LENGTH_LONG)
}
.show()
}
}
)
As you may see from code, only onPermissionGranted() do something particular, and either onPermissionPreviouslyDenied() and onPermissionDisabled() just saying to user boring and common things, which I want to incapsulate to some class that will create either dialogs for extra important things (like location; if permission denied I'd like to close entire app), and either just upload, which will just block functionality.
I know how to do such requests for permission and other stuff like it. I don't know how to create these classes with the dialogs - create enum that I pass whenever I call onDisabled/onPreviouslyDenied method from activity, or create Builder for it, or create Factory... if you TL;DR case, then just answer: 'How to reduce the same code in my case?'
An alternative solution would be to create a BaseActivity class, and have your other activities in the app sub-class the BaseActivity.
Something like..
class BaseActivity: AppCompatActivity {
override fun onCreate() {
super.onCreate()
checkForPermissions() // do your permission check code
}
}
class MainActivity: BaseActivity {
override fun onCreate() {
super.onCreate() // calls BaseAcivitiy's onCreate, which triggers the checkForPermissions
}
}
As CommonsWare suggested in the comments, there is (here is libraries to try to reduce some of this boilerplate) a lot of good libraries. I've chosen a NoPermission library. Maybe, it seems like an advertising, but I'm sure that this is not the worst library. Have a nice day!!!
I suggest use the following library:
https://github.com/Karumi/Dexter
On the other hand, to avoid duplicate code, you can move to separate class the code related to dialog helper.
For instance: https://github.com/jpgpuyo/MVPvsMVVM/blob/one_dialog/app/src/main/java/upday/mvpvsmvvm/dialoghelper/DialogHelper.java

Why my isLocationEnabled need SuppressLint(MissingPermission) in EasyPermissions?

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.

Categories

Resources