Kotlin - Internet and location permissions problem - android

Ι 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
}
}
}
}
}

Related

Location Permission Prompt is not showing up

`I want to show a permission prompt to get location permission from user.
But my prompt is not showing up in the app.
I have tried many codes from different sites but nothing worked.
Here is my code....
`private fun requestpermission(){
islocationpermissiongranted = ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
backgroundlocationpermission = ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
val permissionRequest : MutableList<String> = ArrayList()
if(!islocationpermissiongranted){
permissionRequest.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
if(!backgroundlocationpermission){
permissionRequest.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
}
if(permissionRequest.isNotEmpty()){
permissionLauncher.launch(permissionRequest.toTypedArray())
}
}
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, true)
super.onCreate(savedInstanceState)
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){
permissions ->
islocationpermissiongranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: islocationpermissiongranted
backgroundlocationpermission = permissions[Manifest.permission.ACCESS_BACKGROUND_LOCATION] ?: backgroundlocationpermission
}
requestpermission()
}`
First of all you need to register permission of location in the manifest and if you already added that permission then here is another code and it's work perfectly fine
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "Location"
if (ContextCompat.checkSelfPermission(this#MainActivity,
android.Manifest.permission.ACCESS_FINE_LOCATION) !==
PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this#MainActivity,
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
ActivityCompat.requestPermissions(this#MainActivity,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 1)
} else {
ActivityCompat.requestPermissions(this#MainActivity,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 1)
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
1 -> {
if (grantResults.isNotEmpty() && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
if ((ContextCompat.checkSelfPermission(this#MainActivity,
android.Manifest.permission.ACCESS_FINE_LOCATION) ===
PackageManager.PERMISSION_GRANTED)) {
Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show()
}
return
}
}
}
This is a permission of location in manifest file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:allowBackup="true"
android:dataExtractionRules="#xml/data_extraction_rules"

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?

Why doesn't camerax sample project put constant inside a class?

The Code A is from camerax sample project.
PERMISSIONS_REQUEST_CODE is a constant, I think it would be put inside a class, just like Code B.
I think Code B can reduce the couple and more clear
Is the Code A better Code B?
Code A
private const val PERMISSIONS_REQUEST_CODE = 10
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
class PermissionsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!hasPermissions(requireContext())) {
// Request camera-related permissions
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
// If permissions have already been granted, proceed
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
}
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if (PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
// Take the user to the success fragment when permission is granted
Toast.makeText(context, "Permission request granted", Toast.LENGTH_LONG).show()
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
} else {
Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
}
}
}
...
}
Code B
private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
class PermissionsFragment : Fragment() {
private val PERMISSIONS_REQUEST_CODE = 10
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!hasPermissions(requireContext())) {
// Request camera-related permissions
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
// If permissions have already been granted, proceed
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
}
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if (PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
// Take the user to the success fragment when permission is granted
Toast.makeText(context, "Permission request granted", Toast.LENGTH_LONG).show()
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
} else {
Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
}
}
}
...
}
By adding request code as compile-time constant, it can be accessed without keeping an instance of the class(like static modifier in Java).
Properties the value of which is known at compile time can be marked as compile time constants using the const modifier. Such properties need to fulfil the following requirements:
Top-level, or member of an object declaration or a companion object.
Initialized with a value of type String or a primitive type
No custom getter
Such properties can be used in annotations
The code sample makes a fragment with sole responsibility of handling Camera permission. While onActivityResult() is handled inside fragment, there might be a scenario later on when you want to do some work in Parent Activity based on the result, like for example removing this fragment when the request is complete. In order to do that, you would need access to this request code in Activity. By adding request code as compile-time constant, it can be accessed without keeping an instance of the fragment.
Other than that, there is no merit/demerit in either approaches since you will only making one instance of this fragment throughout your app anyway.
Another option would be to put the constant inside a companion object. This will allow you to make the PERMISSIONS_REQUEST_CODE constant AND statically accessible without the need to have an instant of PermissionsFragment.
Like this:
class PermissionsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!hasPermissions(requireContext())) {
// Request camera-related permissions
requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
} else {
// If permissions have already been granted, proceed
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
}
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
if (PackageManager.PERMISSION_GRANTED == grantResults.firstOrNull()) {
// Take the user to the success fragment when permission is granted
Toast.makeText(context, "Permission request granted", Toast.LENGTH_LONG).show()
Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
PermissionsFragmentDirections.actionPermissionsToCamera())
} else {
Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
}
}
}
companion object {
private const val PERMISSIONS_REQUEST_CODE = 10
}
}
Outside of PermissionsFragment you would be able to access it by writing PermissionsFragment.PERMISSIONS_REQUEST_CODE if you remove the private visibility modifier.

ActivityCompat onRequestPermissionsResult() not Called

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

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.

Categories

Resources