I have implemented Runtime Permission. For testing purpose I have denied location permission and then again I have denied the permission with "Never to ask again" checkbox.
Now there is the problem. OnRequestPermissionResult is being called again and again from the system. For this, I can't show a dialog or snackbar on the screen properly. Here is my implementation. Where is the problem?
Checking location permission
override fun onResume() {
super.onResume()
if (checkLocationPermission())
startLocationUpdates()
updateUI()
}
This is my location update call
#SuppressLint("MissingPermission")
private fun startLocationUpdates() {
// Begin by checking if the device has the necessary location settings.
mLocationSettingsClient.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(this, {
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
updateUI()
})
.addOnFailureListener(this, {
handlingLocationClientSettingsFailure(it)
});
}
private fun handlingLocationClientSettingsFailure(it: Exception) {
val apiException = it as ApiException
when (apiException.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
try {
Lg.d(TAG, "Inside start location update method FAILURE: REQUIRED")
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
val rae = it as ResolvableApiException
rae.startResolutionForResult(this#MainActivity, REQUEST_CHECK_SETTINGS);
} catch (sie: IntentSender.SendIntentException) {
Log.i(TAG, "PendingIntent unable to execute request.");
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
val errorMessage = "Location settings are inadequate, and cannot be " +
"fixed here. Fix in Settings.";
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
mRequestingLocationUpdates = false;
}
}
updateUI()
}
override fun onStop() {
super.onStop()
stopLocationUpdates()
}
private fun stopLocationUpdates() {
if (!mRequestingLocationUpdates) {
Lg.d(TAG, "Permission Denied! So No op!!!")
return
}
mFusedLocationClient.removeLocationUpdates(mLocationCallback)
.addOnCompleteListener(this) { mRequestingLocationUpdates = false }
}
private val MY_PERMISSIONS_REQUEST_LOCATION = 99;
private fun startLocationPermissionRequest() {
ActivityCompat.requestPermissions(this#MainActivity,
arrayOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION),
MY_PERMISSIONS_REQUEST_LOCATION);
}
private fun checkLocationPermission(): Boolean {
if (ContextCompat.checkSelfPermission(this#MainActivity, ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this#MainActivity, ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
showSnackbar(R.string.location_permission_rationale,
android.R.string.ok,
View.OnClickListener {
startLocationPermissionRequest()
})
} else {
// No explanation needed, we can request the permission.
startLocationPermissionRequest()
}
return false
} else {
// Permission has already been granted
return true
}
}
#SuppressLint("NeedOnRequestPermissionsResult")
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_LOCATION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// location-related task you need to do.
if (ContextCompat.checkSelfPermission(this#MainActivity, ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this#MainActivity, ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
setLocationEnabled()
startLocationUpdates()
}
} else {
val showRationale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
shouldShowRequestPermissionRationale(permissions[0])
} else {
TODO("VERSION.SDK_INT < M")
}
if (!showRationale) {
// user also CHECKED "never ask again"
// you can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting
showingNecessaryMsgForLocationPermissionDenied()
} else {
// user did NOT check "never ask again"
// this is a good place to explain the user
// why you need the permission and ask if he wants
// to accept it (the rationale)
}
}
return
}
// other 'case' lines to check for other
// permissions this app might request
}
}
private fun showingNecessaryMsgForLocationPermissionDenied() {
Lg.d("TestTag", "Called")
MaterialDialog.Builder(this)
.title("Permission")
.content(R.string.permission_denied_explanation)
.onPositive { dialog, which ->
// Build intent that displays the App settings screen.
val intent = Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", APPLICATION_ID, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
}
.positiveText(R.string.ok)
.show()
}
In this showingNecessaryMsgForLocationPermissionDenied() function I put a log, in my logcat I see that this function called again and again. How to stop it? Here is the logcat screentshot
I want to show this dialog when user denied permission. But I can't draw this smoothly for the above problem.
Note: I have tested the google repository of location update project. Same problem
Based on the documentation of Activity.requestPermissions
This method may start an activity allowing the user to choose which permissions to grant and which to reject. Hence, you should be prepared that your activity may be paused and resumed. Further, granting some permissions may require a restart of your application.
In this code, you are calling checkLocationPermission() in onResume(). Then from checkLocationPermission() call goes to requestPermissions which takes Activity in onPasue(). After onRequestPermissionsResult(), onResume() gets called again, creating infinte loop.
I would suggest having a boolean variable to decide if to call to checkLocationPermission() required.
var isRequestRequired = true;
override fun onResume() {
super.onResume()
if (isRequestRequired && checkLocationPermission())
startLocationUpdates()
updateUI()
}
then update isRequestRequired to false before showingNecessaryMsgForLocationPermissionDenied()
isRequestRequired = false
showingNecessaryMsgForLocationPermissionDenied()
This will stop onResume() from calling checkLocationPermission() again and again
Related
I tried to implement request permissions for writing and reading from storage. Everything worked good but today Android showed me that the method onRequestPermissionsResult(...) is deprecated. There are so many questions about this topic in StackOverflow, but unfortunately, they are outdated.
I called the methods below in a fragment.
It was suggested simply to call:
requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
StorageKeys.STORAGE_PERMISSION_CODE)
instead of my approach:
ActivityCompat.requestPermissions(getActivity(),
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
StorageKeys.STORAGE_PERMISSION_CODE))
But both of them show that onRequestPermissionsResult(...) is deprecated.
Here is my onRequestPermissionsResult(...)-method:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode == StorageKeys.STORAGE_PERMISSION_CODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
exportBibTex.createBibFile();
exportBibTex.writeBibFile(exportBibTex
.getBibDataLibrary(libraryModel, bookDao, noteDao));
Toast.makeText(getContext(),
getString(R.string.exported_file_stored_in) + '\n'
+ File.separator + StorageKeys.DOWNLOAD_FOLDER + File.separator + fileName
+ StorageKeys.BIB_FILE_TYPE, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getContext(), R.string.storage_permission_denied,
Toast.LENGTH_SHORT).show();
}
}
}
Here is a simple alert dialog, in which I call the onRequestPermissionsResult(...):
private void showRequestPermissionDialog() {
AlertDialog.Builder reqAlertDialog = new AlertDialog.Builder(getContext());
reqAlertDialog.setTitle(R.string.storage_permission_needed);
reqAlertDialog.setMessage(R.string.storage_permission_alert_msg);
reqAlertDialog.setPositiveButton(R.string.ok,
(dialog, which) -> ActivityCompat.requestPermissions(getActivity(),
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
StorageKeys.STORAGE_PERMISSION_CODE));
reqAlertDialog.setNegativeButton(R.string.cancel,
(dialog, which) -> dialog.dismiss());
reqAlertDialog.create().show();
}
Is there any alternative for onRequestPermissionsResult(...), that I can use?
The onRequestPermissionsResult() method is deprecated in androidx.fragment.app.Fragment.
So you may use registerForActivityResult() method instead of onRequestPermissionsResult().
You can refer this URL.
Following is Kotlin code, but you can refer it:
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
// PERMISSION GRANTED
} else {
// PERMISSION NOT GRANTED
}
}
// Ex. Launching ACCESS_FINE_LOCATION permission.
private fun startLocationPermissionRequest() {
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
I added java code from following URL.
How to get a permission request in new ActivityResult API (1.3.0-alpha05)?
private ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
new ActivityResultCallback<Boolean>() {
#Override
public void onActivityResult(Boolean result) {
if (result) {
// PERMISSION GRANTED
} else {
// PERMISSION NOT GRANTED
}
}
}
);
// Ex. Launch the permission window -- this is in onCreateView()
floatingActionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION);
}
});
You can also request multiple permissions:
val requestMultiplePermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
permissions.entries.forEach {
Log.d("DEBUG", "${it.key} = ${it.value}")
}
}
requestMultiplePermissions.launch(
arrayOf(
Manifest.permission.READ_CONTACTS,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
A simple way in Kotlin
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
class MyFragment : Fragment() {
companion object {
val TAG: String = MyFragment::class.java.simpleName
var PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
private val permReqLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val granted = permissions.entries.all {
it.value == true
}
if (granted) {
displayCameraFragment()
}
}
private fun takePhoto() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
displayCameraFragment()
return
}
activity?.let {
if (hasPermissions(activity as Context, PERMISSIONS)) {
displayCameraFragment()
} else {
permReqLauncher.launch(
PERMISSIONS
)
}
}
}
// util method
private fun hasPermissions(context: Context, permissions: Array<String>): Boolean = permissions.all {
ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
private fun displayCameraFragment() {
// open camera fragment
}
}
Alternative registerForActivityResult
Example use:
private fun permissionSetup() {
val permission = ContextCompat.checkSelfPermission(
requireContext(), Manifest.permission.READ_CONTACTS)
if (permission != PackageManager.PERMISSION_GRANTED) {
permissionsResultCallback.launch(Manifest.permission.READ_CONTACTS)
} else {
println("Permission isGranted")
}
}
private val permissionsResultCallback = registerForActivityResult(
ActivityResultContracts.RequestPermission()){
when (it) {
true -> { println("Permission has been granted by user") }
false -> {
Toast.makeText(requireContext(), "Permission denied", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
}
}
Register the permissions callback in your activity or fragment. which handle user permission
Example-Storage permission
private final ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result) {
//Permission granted
} else {
//permission denied
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, WRITE_EXTERNAL_STORAGE)) {
//show permission snackbar
} else {
//display error dialog
}
}
});
Ask for the permission.The registered ActivityResultCallback gets the result of this request.
if (checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PermissionChecker.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE);
} else {
//Permission already granted
}
This works for me - (kotlin):
private fun checkPermissions() {
if (mContext?.let {
ContextCompat.checkSelfPermission(
it,
READ_EXTERNAL_STORAGE
)
} != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Request Permissions")
requestMultiplePermissions.launch(
arrayOf(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE))
} else {
Log.d(TAG, "Permission Already Granted")
}
}
private val requestMultiplePermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.entries.forEach {
Log.d(TAG, "${it.key} = ${it.value}")
}
if (permissions[READ_EXTERNAL_STORAGE] == true && permissions[WRITE_EXTERNAL_STORAGE] == true) {
Log.d(TAG, "Permission granted")
} else {
Log.d(TAG, "Permission not granted")
}
}
Most of the answers address the OP requirement. But I have found few things that are missing so I thought to provide a complete example (in Koltin)
class ProfileFragment : Fragment(){
private lateinit var permissionRequest : ActivityResultLauncher<Array<String>>
companion object {
val LOCATION_PERMISSIONS = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
}
private fun getGpsLocation() {
if(activity != null){
permissionRequest.launch(LOCATION_PERMISSIONS)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.locBtn.setOnClickListener { getGpsLocation() }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerPermissionRequest()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_profile, container, false)
return binding.root
}
private fun registerPermissionRequest(){
var permissionCount = 0
permissionRequest = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.entries.forEach {
if(it.value){
permissionCount++
}
}
if(permissionCount == 2){
getMyLocation()
}
}
}
}
The things that are missing are
A)Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()). Other wise it won't work and the app would crash.
Error:
java.lang.IllegalStateException: Fragment ProfileFragment{bf12414} (210ad5a1-3286-4586-a48f-deac1d8e3eef
id=0x7f09008b) is attempting to registerForActivityResult after being
created. Fragments must call registerForActivityResult() before they
are created (i.e. initialization, onAttach(), or onCreate()).
B) It is recommended to request permission when it is really needed. In my example when user clicks on Button with id locBtn, permission dialog is shown rather than showing when activity/fragment is created.
Please refer to the official documentation: https://developer.android.com/training/permissions/requesting
Review the following, which can be found in the documentation.
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher. You can use either a val, as shown in this snippet,
// or a lateinit var in your onAttach() or onCreate() method.
val requestPermissionLauncher =
registerForActivityResult(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
// features 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.
}
}
After which, launch the request
when {
ContextCompat.checkSelfPermission(
CONTEXT,
Manifest.permission.REQUESTED_PERMISSION
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
}
//Is not needed for it to work, but is a good practice as it plays a role
//in letting user know why the permission is needed.
shouldShowRequestPermissionRationale(...) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected. In this UI,
// include a "cancel" or "no thanks" button that allows the user to
// continue using your app without granting the permission.
showInContextUI(...)
}
else -> {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION)
}
}
Suppose we need to take some permissions like audio and camera. Then we can put it on a variable of array.
private val REQUESTED_PERMISSIONS =
arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
This registerForActivityResult invoke when we launch with multiple permission array.
private val requestMultiplePermissions = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
permissions.entries.forEach {
Log.d("DEBUG", "${it.key} = ${it.value}")
if (!it.value){
return#registerForActivityResult
}
}
init()
}
Now we make a function to check permission is already granted or not.
private fun checkSelfPermission(): Boolean {
return !(ContextCompat.checkSelfPermission(
requireContext(),
REQUESTED_PERMISSIONS[0]
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
requireContext(),
REQUESTED_PERMISSIONS[1]
) != PackageManager.PERMISSION_GRANTED)
}
Now we check permission is granted or not from onCreate method. If permission is not granted then we invoke requestMultiplePermissions.launch(REQUESTED_PERMISSIONS) like
super.onCreate(savedInstanceState)
if (!checkSelfPermission()) {
requestMultiplePermissions.launch(REQUESTED_PERMISSIONS)
}
}
You can use some external library for permission handling to reduce some boilerplate code. I use Dexter library. Rx Permissions is also good choice if you are using RxJava2.
Try this in in your pubspec.yamal under flutter section:
plugin:
androidPackage: com.ly.permission
pluginClass: PermissionPlugin
It works with me.
Here's my code:
private var longitude = ""
private var latitude = ""
In the oncreateView, I have to do a api call with my latitude and longitude data. But the value of latitude and longitude never change!
//location
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireActivity())
// getCurrentLocation
checkPermissions()
//CALL API
val request = Request.Builder()
.url(" API_PATH?latitude=$latitude&longitude=$longitude")
.build()
val client = OkHttpClient()
...
private fun checkPermissions(){
if(ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION) !=
PackageManager.PERMISSION_DENIED){
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 1)
}
else{
getLocations()
}
}
private fun getLocations() {
if (ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
fusedLocationProviderClient.lastLocation.addOnCompleteListener {
if(it == null){
Toast.makeText(requireContext(), "sorry can't get the location", Toast.LENGTH_SHORT).show()
}else it.apply {
latitude = it.result.latitude.toString()
longitude = it.result.longitude.toString()
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode== 1){
if(grantResults.isNotEmpty() && grantResults[0]==PackageManager.PERMISSION_GRANTED){
//permission granted
if(ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION) ==
PackageManager.PERMISSION_GRANTED){
Toast.makeText(requireContext(), "Permission granted", Toast.LENGTH_SHORT).show()
getLocations()
}
}else{
Toast.makeText(requireContext(), "Permission denied", Toast.LENGTH_SHORT).show()
//denied permission
}
}
}
First, I enter always in th first condition in checkPermissions(). I don't know why. Secondly, I also try to write getLocations() in the first condition in the first if in checkPermissions(). But longitude and latitude are never update.
Can you help me please, I am on this case for a long time and I have no idea.
I think you always enter the first condition in checkPermissions() because you haven't yet granted the permissions.
I think you need to request the permissions where the TODO code is commented out.
It will be something like:
// You can directly ask for the permission.
requestPermissions(CONTEXT,
arrayOf(Manifest.permission.REQUESTED_PERMISSION),
REQUEST_CODE)
Found from https://developer.android.com/training/permissions/requesting
I hope that steers you in the right direction.
If you want to cheat for testing, you can go to Settings -> Apps -> Your app -> Permissions and enable the permissions and then re-test. Of course, you'll have to do the request in code for the users of your app before you release the app.
I have this simple activity which popups the location permission dialog and based on negative or positive permission dialog button interaction I clear the activity.As a user when I dismiss the dialog for the second time and come back to the app again the dialog doesn't show up anymore and the activity just finishes right away. I would want to show the permission dialog every-time if the permission was not granted
override fun onResume() {
super.onResume()
setupPermission()
}
private fun setupPermission() {
val permission = PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
if (permission != PackageManager.PERMISSION_GRANTED) {
permissionsResultCallback.launch(Manifest.permission.ACCESS_FINE_LOCATION)
} else {
Log.d("dawg", "already granted")
finish()
}
}
private val permissionsResultCallback = registerForActivityResult(
ActivityResultContracts.RequestPermission()) { granted ->
when (granted) {
true -> {
Log.d("dawg", "granted now via dialog")
finish()
}
false -> {
Log.d("dawg", "denied via dialog")
finish()
}
}
}
You can use shouldShowRequestPermissionRationale. If the user deny for 2nd time you can show a snack bar where user can goto settings page and turn on notification manually.
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
showSnackBarForOpenSettings(
context,
"Camera Permission needed",
view
)
}
} else {
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(android.Manifest.permission.CAMERA), 1
)
}
For opening app settings screen you can use this:
fun Context.openAppSystemSettings() {
startActivity(Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.fromParts("package", packageName, null)
})
}
use it like this context.openAppSystemSettings()
Before a few days, the code is working fine not having issues with permissions.
I am not able to grant permission at run-time also having issues while granting permissions from the setting. (App permission detail page).
var permissions = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
} else {
arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
}
Requesting For Permissions on the appropriate button click.
permissionResultLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
var allPermissionGranted = true
permissions.entries.forEach {
if (!it.value) {
Log.e(TAG, "Permission ${it.key} granted : ${it.value}")
allPermissionGranted = false
}
}
if (!permissionDeniedDialog && !allPermissionGranted) {
showDialog(
"Required",
"App needs Bluetooth and Location permissions to scan bluetooth devices.",
"Later",
"Allow",
object : DialogView.ButtonListener {
override fun onNegativeButtonClick(dialog: AlertDialog) {
dialog.dismiss()
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", requireActivity().applicationContext.packageName, null))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
override fun onPositiveButtonClick(dialog: AlertDialog) {
dialog.dismiss()
requestRequirdPermissions()
}
})
} else {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", requireActivity().applicationContext.packageName, null))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
permissionDeniedDialog = true
}
permissionResultLauncher.launch(permissions)
Function for checking permissions
private fun checkPermission(): Boolean {
for (permission in permissions) {
if (ContextCompat.checkSelfPermission(requireContext(), permission) != PackageManager.PERMISSION_GRANTED) return false
}
return true
}
Can someone let me know why the above not working?. the system was on the result directly and the app was redirecting the app to the setting page, but I am not able to grant permission from the settings page too.
Is this OS Specific?, anyone having the same issues with ColorOS 11 ?.
Please guide me if anything is missing from my side.
Device: OPPO F17 Pro
OS: Color OS 11, Android 11 Based
Note:
Above code, with Samsung device, Android 11 Based (OneUI 3.1), App was not asking for runtime but after redirecting on setting page, I am granting location permission and app was working fine, regarding OPPO i am not able grant permission from setting page.
What tried :
var permissions = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
} else {
arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION)
}
private fun requestRequirdPermissions() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
permissionResultLauncher.launch(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
}
else
{
permissionResultLauncher.launch(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION ))
}
}
Try this way may help you
fun checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var permissions: ArrayList<String> = ArrayList<String>()
permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
val listPermissionsNeeded: ArrayList<String> = ArrayList()
for (p in permissions) {
var result = ContextCompat.checkSelfPermission(this, p!!)
if (result != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(p)
}
}
if (!listPermissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(
this,
listPermissionsNeeded.toTypedArray(),
CODE
)
} else {
next()
}
} else {
next()
}
}
override fun onRequestPermissionsResult(
requestc: Int,
permissions: Array<String>,
grantRes: IntArray
) {
super.onRequestPermissionsResult(requestc, permissions, grantRes);
when (requestc) {
CODE -> {
var isGrant = true
if (grantRes.size > 0) {
for (i in 0 until grantResults.size) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
isGrant = false
}
}
}
if (isGrant) {
next()
} else {
checkRationalePermission(permissions)
}
}
}
}
var alertDialogRatinal: android.app.AlertDialog? = null
fun checkRationale(permissions: Array<String>) {
if (alertDialogRatinal != null && alertDialogRatinal!!.isShowing || permissions.size == 0) {
return
}
var someDenied = false
for (permission in permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this#Activity,
permission!!
)
) {
} else {
if (ActivityCompat.checkSelfPermission(
this#Activity,
permission
) == PackageManager.PERMISSION_GRANTED
) {
} else {
someDenied = true
}
}
}
if (someDenied) {
val alertDialogBuilder =
android.app.AlertDialog.Builder(this#Activity)
alertDialogRatinal = alertDialogBuilder.setTitle("Permissions Required")
.setMessage(
"Please open settings, go to permissions and allow them."
)
.setPositiveButton(
"Settings"
) { dialog, which ->
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts(
"package",
this#Activity.getPackageName(),
null
)
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityForResult(intent, 100)
}
.setNegativeButton(
"Cancel"
) { dialog, which -> }
.setCancelable(false)
.create()
alertDialogRatinal!!.show()
}
}
My issue was fixed by the system reset, but according to comments and documents please follow the below guidelines for the location permission.
Thanks Pawel for comments,
Ref: https://stackoverflow.com/a/66321942/9909365
Background location permission does not work like other permissions. It's a request to elevate location permission from foreground-only to foreground & background.
User has to consciously select "Allow all the time" in order to do that and grant background location permission. Otherwise that permission is considered denied.
You cannot even request background location unless foreground location is already granted - when system permission activity shows up it should already have option 2 or 3 selected.
See https://developer.android.com/training/location/permissions#request-background-location
Note:
When your app is using this background location please be prepared with a short video that demonstrates the location-based feature in your app that requires access to location in the background (while the app is not in use).
See https://support.google.com/googleplay/android-developer/answer/9799150
private val requestIdMultiplePermissions = 1
private val permissionsRequest: ArrayList<String> =
arrayListOf(READ_CALENDAR
, CAMERA)
findViewById<Button>(R.id.multiplePermissionBtn).setOnClickListener {
if (checkMultipleRequestPermissions()) {
doOperation()
}
}
private fun doOperation() {
Toast.makeText(this, "Successfully granted", Toast.LENGTH_LONG).show()
}
private fun checkMultipleRequestPermissions(): Boolean {
val listPermissionsNeeded: MutableList<String> = ArrayList()
for (p in permissionsRequest) {
val result = ContextCompat.checkSelfPermission(this, p)
if (result != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(p)
}
}
if (listPermissionsNeeded.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
listPermissionsNeeded.toTypedArray(),
requestIdMultiplePermissions
)
return false
}
return true
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == requestIdMultiplePermissions) {
if (grantResults.isNotEmpty()) {
var isGrant = true
for (element in grantResults) {
if (element == PackageManager.PERMISSION_DENIED) {
isGrant = false
}
}
if (isGrant) {
doOperation()
} else {
var someDenied = false
for (permission in permissions) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(
this,
permission
)
) {
if (ActivityCompat.checkSelfPermission(
this,
permission
) == PackageManager.PERMISSION_DENIED
) {
someDenied = true
}
}
}
if (someDenied) {
settingActivityOpen()
}else{
showDialogOK { _: DialogInterface?, which: Int ->
when (which) {
DialogInterface.BUTTON_POSITIVE -> checkMultipleRequestPermissions()
DialogInterface.BUTTON_NEGATIVE -> { }
}
}
}
}
}
}
}
private fun settingActivityOpen() {
Toast.makeText(
this,
"Go to settings and enable permissions",
Toast.LENGTH_LONG
)
.show()
val i = Intent()
i.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
i.addCategory(Intent.CATEGORY_DEFAULT)
i.data = Uri.parse("package:$packageName")
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
startActivity(i)
}
private fun showDialogOK(okListener: DialogInterface.OnClickListener) {
MaterialAlertDialogBuilder(this)
.setMessage("All Permission required for this app")
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show()
}
I'm using google maps so I need to ask some permissions.
I'm requesting the user during the onStart() of the Fragment
I'm calling requestPermissions because I'm in a fragment and I also have called super.onRequestPermissionsResult as recommended in this answer and as you can see in the code below.
I have put some breakpoint and Log to be 100% sure that onRequestPermissionsResult is not called.
class FindLessonFragment : BaseFragment(), OnMapReadyCallback, OnClusterItemClickListener<ClusterLesson>, OnClusterClickListener<ClusterLesson> {
override fun onStart() {
super.onStart()
checkPermissions()
}
private fun checkPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (activity!!.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| activity!!.checkSelfPermission(permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| activity!!.checkSelfPermission(permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
val builder = AlertDialog.Builder(activity!!)
builder.setTitle("This app needs location access")
builder.setMessage("Please grant location access so this app can work normally.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener {
requestPermissions(
arrayOf(permission.ACCESS_COARSE_LOCATION, permission.READ_EXTERNAL_STORAGE, permission.ACCESS_FINE_LOCATION),
PERMISSION_REQUEST_LOCATION)
}
builder.show()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, #NonNull permissions: Array<String>, #NonNull grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Log.e("PERMISSIONRESULT", "PASS PERMISSION RESULT")
when (requestCode) {
BaseActivity.PERMISSION_REQUEST_LOCATION -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("BaseActivity", "coarse location permission granted")
onMapReady(mMap)
} else {
val builder = AlertDialog.Builder(context!!)
builder.setTitle("Functionality limited")
builder.setMessage(
"Since location access has not been granted, this app will not be able to work correctly.")
builder.setPositiveButton(android.R.string.ok, null)
builder.setOnDismissListener { dialog -> dialog.dismiss() }
builder.show()
}
}
}
}
companion object {
fun create() = FindLessonFragment()
internal const val PERMISSION_REQUEST_LOCATION = 1
}
}
minSdkVersion 23
Any kind of help is welcome
The onRequestPermissionsResult has to be catch in the activty.