I have a function in Kotlin that enables GPS in app. Is that possible to change dialog box by adding a checkbox with a parameter like "Never ask again" in order to avoid turning on GPS all the times when user open app?
Or maybe there is a kind of another solution like making a custom dialog box?
Here is a code:
private fun buildAlertMessageNoGps() {
val mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(2000)
.setFastestInterval(1000)
val settingsBuilder = LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest)
.setAlwaysShow(true)
settingsBuilder.setAlwaysShow(true)
val result = LocationServices.getSettingsClient(this).checkLocationSettings(settingsBuilder.build())
result.addOnCompleteListener { task ->
try {
task.getResult(ApiException::class.java)
} catch (ex: ApiException) {
when (ex.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
val resolvableApiException = ex as ResolvableApiException
resolvableApiException.startResolutionForResult(this, 100)
} catch (e: IntentSender.SendIntentException) {
Toast.makeText(this,"PendingIntent unable to execute request.",Toast.LENGTH_SHORT).show()
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
Toast.makeText(
this,
"Something is wrong in your GPS",
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
According to Android Developers:
If the device is running Android 6.0 or higher, and your app's target SDK is 23 or higher, the app has to list the permissions in the manifest and request those permissions at run time. For more information, see Requesting Permissions at Run Time.
If your app targets Android 10 (API level 29) or higher and needs to access the device location while your app is in the background, you must also declare the ACCESS_BACKGROUND_LOCATION permission. To learn more, see the section on how to request access to background location.
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
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 have simple fragment that aim to fetch user current location.
The error I got is clear, we need to check if the user grant us location permissions:
Call requires permission which may be rejected by user: code should
explicitly check to see if permission is available (with
checkPermission) or explicitly handle a potential SecurityException
But I already made permission check, basically the fetch couldn't be done without user granting us permission, so why do I need to check twice?
My flow is like this:
if (activityListener.checkPermissions()) {
showMapAndFetchLocation()
}
Inside showMapAndFetchLocation method
initLocationProvider()
val mapFragment =
childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(callback)
And last, inside initLocationProvider method
if (isLocationEnabled()) {
fusedLocationClient //**Error**
.lastLocation //**Error**
.addOnCompleteListener { task: Task<Location> ->
val location = task.result
location?.let {
Log.d(TAG, "getUserLastLocation: LAT ${it.latitude}")
Log.d(TAG, "getUserLastLocation: LNG ${it.longitude}")
}
?: requestNewLocationData()
}
}
As you can see, I'm checking frist if (activityListener.checkPermissions()) and only then fire the fetching process.
Do I really need to check twice or am I doing something wrong?
As #Pawel Suggested, I needed to add try/catch block and catch SecurityException exception.
Now the method looks like this:
try {
fusedLocationClient
.lastLocation
.addOnCompleteListener { task: Task<Location> ->
val location = task.result
location?.let {
Log.d(TAG, "getUserLastLocation: LAT ${it.latitude}")
Log.d(TAG, "getUserLastLocation: LNG ${it.longitude}")
setLngLat(it.latitude, it.longitude)
initMap()
}
?: requestNewLocationData()
}
} catch (e: SecurityException) {
activityListener.isLocationPermissionGranted()
}
I used this class to get current location of device for my map app. I'm using this with GooglePlayServices and its working fine, but I recently switched to HMS for Huawei devices if GooglePlayServices are not available on device. I replaced all GooglePlayServices classes with mirror objects from HMS imported lib and it compiled without errors. But as I call for current location, it will not return anything. No exception, no success or failure.
I did not receive callback to onLocationResult() or catch() block.
According to debugger last row called is val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
Anyone has this problem? This is clearly new issue. Testing this on Huawei P40 where GooglePlayServices are not available.
Also HuaweiMap is not working in release mode. getMapAsync() will not return onMapReady() callback. It got stuck there. But if I switch debug mode, it is working correctly.
UDPATE:
HuaweiMap is working now. Updated proguard. But Location is still not working. It is not working even in debug mode.
Code:
private inner class LocationCbHua(val lp: com.huawei.hms.location.FusedLocationProviderClient,
val onFailure: (()->Unit)? = null,
val onSuccess: (GpsLocation)->Unit)
: com.huawei.hms.location.LocationCallback() {
init {
val lr = com.huawei.hms.location.LocationRequest.create().apply {
priority = com.huawei.hms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 200
}
val lsr = com.huawei.hms.location.LocationSettingsRequest.Builder().run {
// setAlwaysShow(true) // TEST
addLocationRequest(lr)
build()
}
val check = com.huawei.hms.location.LocationServices.getSettingsClient(activity!!).checkLocationSettings(lsr)
check.addOnCompleteListener {
try {
check.getResultThrowException(com.huawei.hms.common.ApiException::class.java)
val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
task.addOnFailureListener {
onFailure?.invoke()
}
} catch (e: com.huawei.hms.common.ApiException) {
when (e.statusCode) {
com.huawei.hms.location.LocationSettingsStatusCodes.RESOLUTION_REQUIRED-> if(!locationResolutionAsked){
// Location settings are not satisfied. But could be fixed by showing the user a dialog.
try {
// Cast to a resolvable exception.
val re = e as com.huawei.hms.common.ResolvableApiException
// Show the dialog by calling startResolutionForResult(), and check the result in onActivityResult().
re.startResolutionForResult(mainActivity, MainActivity.REQUEST_LOCATION_SETTINGS)
locationResolutionAsked = true
} catch (e: Exception) {
e.printStackTrace()
}
}
com.huawei.hms.location.LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE->{
// Location settings are not satisfied. However, we have no way to fix the settings so we won't show the dialog.
App.warn("Location is not available")
onFailure?.invoke()
}
}
}
}
}
fun cancel(){
lp.removeLocationUpdates(this)
currLocCb = null
}
override fun onLocationResult(lr: com.huawei.hms.location.LocationResult) {
cancel()
val ll = lr.lastLocation
onSuccess(GpsLocation(ll.longitude, ll.latitude))
}
}
The possible cause is as follows:
After checkLocationSettings code was executed, an exception was catched during execution of code check.getResultThrowException. However, the catched error code is not 6 (RESOULTION_REQUIRED).
Therefore, code com.huawei.hms.location.LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE was directly executed to report Location is not available after code com.huawei.hms.location.LocationSettingsStatusCodes.RESOLUTION_REQUIRED-> if(!locationResolutionAsked) was executed.
As a result, neither exception nor location result was obtained. You are advised to add a code line at when (e.statusCode) to record error logs, and then continue error analysis.
Use OnSuccessListener instead of OnCompleteListener
val check = com.huawei.hms.location.LocationServices.getSettingsClient(activity!!).checkLocationSettings(lsr)
check.addOnSuccessListener{
lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
}
You can also check this post:
https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201272177441270079&fid=0101187876626530001
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.