Related
I am creating a simple applications in which I need READ_CONTACT and CALL_PHONE permissions. I have written below code.
After installation app asks permissions 3 times like this -
1 of 2 read contacts
2 of 2 call and manage phone
1 of 2 read contacts
2 of 2 call and manage phone
1 of 2 read contacts
2 of 2 call and manage phone
Also After granting these permissions app does't open. But when I open app again, it works fine and does not ask permissions again.
I have following code
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case MULTIPLE_REQUESTS: {
if (grantResults.length > 0) {
boolean contactPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;
boolean phonePermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (contactPermission && phonePermission) {
// write your logic here
} else {
Toast.makeText(this, "Read Contact & Call phone permissions are required", Toast.LENGTH_SHORT).show();
closeNow();
}
}
break;
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
+ ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale
(this, Manifest.permission.READ_CONTACTS) ||
ActivityCompat.shouldShowRequestPermissionRationale
(this, Manifest.permission.CALL_PHONE)) {
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE},
MULTIPLE_REQUESTS);
}
}
setContentView(R.layout.activity_contact_app_bar);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setTitle(getTitle());
}
The permission should be asked when you invoke respective functionality .
I am suspecting the app is getting closed due to closeNow is called .
You need to debug below code .
if (grantResults.length > 0) {
boolean contactPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;
boolean phonePermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (contactPermission && phonePermission) {
// write your logic here
} else {
Toast.makeText(this, "Read Contact & Call phone permissions are required", Toast.LENGTH_SHORT).show();
closeNow();
}
}
try This Code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE},
MULTIPLE_REQUESTS);
}
add above code where you want permission
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MULTIPLE_REQUESTS: {
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] !=
PackageManager.PERMISSION_GRANTED) {
// Permission has been denied by user
} else {
// Permission has been granted by user
}
return;
}
}
}
}
In the manifest file I added permissions coarse and fine, and when I run on device with Android 6, nothing happens! I try
everything but no way to get location updates...
What am I doing wrong?
public class MainActivity extends AppCompatActivity implements LocationListener {
LocationManager locationManager;
String provider;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
provider = locationManager.getBestProvider(new Criteria(), false);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
Log.i("Location Info", "Location achieved!");
} else {
Log.i("Location Info", "No location :(");
}
}
#Override
protected void onResume() {
super.onResume();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
#Override
protected void onPause() {
super.onPause();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
locationManager.removeUpdates(this);
}
#Override
public void onLocationChanged(Location location) {
Double lat = location.getLatitude();
Double lng = location.getLongitude();
Log.i("Location info: Lat", lat.toString());
Log.i("Location info: Lng", lng.toString());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
public void getLocation(View view) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
Location location = locationManager.getLastKnownLocation(provider);
onLocationChanged(location);
}
}
You need to actually request the Location permission at runtime (notice the comments in your code stating this).
Updated with Kotlin and background location for API 31 (Android 12):
Starting with API 30 background location must be requested separately.
This example is using targetSdk 31 and compileSdk 31.
Note that it's possible to bundle the background location request along with the main location request on API 29, however to do that you would need to maintain three separate code paths.
It's easier to just break it out to separate requests for 29 and above.
Be sure to include the latest location services in the app level gradle (18.0.0 at the time of writing):
implementation "com.google.android.gms:play-services-location:18.0.0"
Include the location permissions in the manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
This is a simplified example that does handle most cases, but in a simplified way. In the case where a user chooses "Don't ask again", on the next app launch it will open up the settings for the user to manually enable the permission.
Full activity code:
import android.Manifest
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
class MainActivity : AppCompatActivity() {
private var fusedLocationProvider: FusedLocationProviderClient? = null
private val locationRequest: LocationRequest = LocationRequest.create().apply {
interval = 30
fastestInterval = 10
priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
maxWaitTime = 60
}
private var locationCallback: LocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val locationList = locationResult.locations
if (locationList.isNotEmpty()) {
//The last location in the list is the newest
val location = locationList.last()
Toast.makeText(
this#MainActivity,
"Got Location: " + location.toString(),
Toast.LENGTH_LONG
)
.show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fusedLocationProvider = LocationServices.getFusedLocationProviderClient(this)
checkLocationPermission()
}
override fun onResume() {
super.onResume()
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}
override fun onPause() {
super.onPause()
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.removeLocationUpdates(locationCallback)
}
}
private fun checkLocationPermission() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
// 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.
AlertDialog.Builder(this)
.setTitle("Location Permission Needed")
.setMessage("This app needs the Location permission, please accept to use location functionality")
.setPositiveButton(
"OK"
) { _, _ ->
//Prompt the user once explanation has been shown
requestLocationPermission()
}
.create()
.show()
} else {
// No explanation needed, we can request the permission.
requestLocationPermission()
}
} else {
checkBackgroundLocation()
}
}
private fun checkBackgroundLocation() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
requestBackgroundLocationPermission()
}
}
private fun requestLocationPermission() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
),
MY_PERMISSIONS_REQUEST_LOCATION
)
}
private fun requestBackgroundLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION
)
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
MY_PERMISSIONS_REQUEST_LOCATION
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_LOCATION -> {
// If request is cancelled, the result arrays are empty.
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,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
// Now check background location
checkBackgroundLocation()
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
// Check if we are in a state where the user has denied the permission and
// selected Don't ask again
if (!ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
startActivity(
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", this.packageName, null),
),
)
}
}
return
}
MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION -> {
// If request is cancelled, the result arrays are empty.
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,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
Toast.makeText(
this,
"Granted Background Location Permission",
Toast.LENGTH_LONG
).show()
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
}
return
}
}
}
companion object {
private const val MY_PERMISSIONS_REQUEST_LOCATION = 99
private const val MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 66
}
}
On Android 10 (API 29) it will give the user the choice to grant background location after the initial location request:
On Android 12 (API 31) it will do the same, but the interface is different:
Original Answer in Java:
Here is tested and working code to request the Location permission.
Put this code in the Activity:
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// 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.
new AlertDialog.Builder(this)
.setTitle(R.string.title_location_permission)
.setMessage(R.string.text_location_permission)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// location-related task you need to do.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Request location updates:
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
}
}
Then call the checkLocationPermission() method in onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//.........
checkLocationPermission();
}
You can then use onResume() and onPause() exactly as it is in the question.
Here is a condensed version that is a bit more clean:
#Override
protected void onResume() {
super.onResume();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
}
#Override
protected void onPause() {
super.onPause();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(this);
}
}
Google has created a library for easy Permissions management. Its called EasyPermissions
Here is a simple example on requesting Location permission using this library.
public class MainActivity extends AppCompatActivity {
private final int REQUEST_LOCATION_PERMISSION = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestLocationPermission();
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
#AfterPermissionGranted(REQUEST_LOCATION_PERMISSION)
public void requestLocationPermission() {
String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION};
if(EasyPermissions.hasPermissions(this, perms)) {
Toast.makeText(this, "Permission already granted", Toast.LENGTH_SHORT).show();
}
else {
EasyPermissions.requestPermissions(this, "Please grant the location permission", REQUEST_LOCATION_PERMISSION, perms);
}
}
}
#AfterPermissionsGranted(REQUEST_CODE) is used to indicate the method that needs to be executed after a permission request with the request code REQUEST_CODE has been granted.
This above case, the method requestLocationPermission() method is called if the user grants the permission to access location services. So, that method acts as both a callback and a method to request the permissions.
You can implement separate callbacks for permission granted and permission denied as well. It is explained in the github page.
Location permission privacy change in Android 10 or Android Q.
We have to define additional ACCESS_BACKGROUND_LOCATION permission if user wants to access their current location in background so user needs to granted permission runtime also in requestPermission()
If we are using lower than Android 10 device then ACCESS_BACKGROUND_LOCATION permission allow automatically with ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission
This tabular format might be easy to understand what if we don't specify ACCESS_BACKGROUND_LOCATION in manifest file.
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> // here we defined ACCESS_BACKGROUND_LOCATION for Android 10 device
MainActivity.java
Call checkRunTimePermission() in onCreate() or onResume()
public void checkRunTimePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
} else {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
10);
}
} else {
gpsTracker = new GPSTracker(context); //GPSTracker is class that is used for retrieve user current location
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 10) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.ACCESS_FINE_LOCATION)) {
// If User Checked 'Don't Show Again' checkbox for runtime permission, then navigate user to Settings
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle("Permission Required");
dialog.setCancelable(false);
dialog.setMessage("You have to Allow permission to access user location");
dialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package",
context.getPackageName(), null));
//i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(i, 1001);
}
});
AlertDialog alertDialog = dialog.create();
alertDialog.show();
}
//code for deny
}
}
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
switch (requestCode) {
case 1001:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
if (gpsTracker.canGetLocation()) {
latitude = gpsTracker.getLatitude();
longitude = gpsTracker.getLongitude();
}
} else {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION},10);
}
}
break;
default:
break;
}
}
build.gradle (app level)
android {
compileSdkVersion 29 //should be >= 29
buildToolsVersion "29.0.2"
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "com.example.runtimepermission"
minSdkVersion 21
targetSdkVersion 29 //should be >= 29
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
}
Here you can find GPSTracker.java file code
Below is my solution to requesting foreground and background location permissions on 28 and below, 29, and 30. The differences between the APIs are subtle but important.
API 28 and below, the system treats foreground and background location permissions as the same. If you grant location permissions, then the application is granted both implicitly.
API 29, you can request both foreground and background permissions at the same time.
API 30, you must request foreground location permissions and then only request background location permissions after the foreground location permissions have been granted. If you ask for both foreground and background permissions at the same time, then the system will ignore the request. Another difference is that the user must allow background location permissions within the application location permission settings rather than through a system dialog.
The solution below only starts the specified action (e.g. background location tracking) only after the user has accepted both foreground and background location tracking:
LocationPermissionUtil.kt
private const val REQUEST_CODE_FOREGROUND = 1
private const val REQUEST_CODE_FOREGROUND_AND_BACKGROUND = 2
object LocationPermissionUtil {
private fun Context.isPermissionGranted(permission: String): Boolean = ActivityCompat
.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
private val Context.isFineLocationPermissionGranted
get() = isPermissionGranted(
Manifest.permission.ACCESS_FINE_LOCATION
)
private val Context.isBackgroundPermissionGranted
get() = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
else -> isFineLocationPermissionGranted
}
private val Context.isFineAndBackgroundLocationPermissionsGranted
get() = isFineLocationPermissionGranted && isBackgroundPermissionGranted
private fun Activity.checkFineLocationPermission() {
if (isFineLocationPermissionGranted) return
val shouldShowFineLocationPermissionRationale = ActivityCompat
.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (shouldShowFineLocationPermissionRationale) {
presentAlertDialog(
R.string.dialog_fine_location_rationale_title,
R.string.dialog_fine_location_rationale_description,
R.string.yes,
) {
requestLocationPermissions()
}
} else {
requestLocationPermissions()
}
}
private fun Activity.requestLocationPermissions() =
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
requestFineLocationAndBackground()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE_FOREGROUND
)
}
#TargetApi(29)
private fun Activity.requestFineLocationAndBackground() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
REQUEST_CODE_FOREGROUND_AND_BACKGROUND
)
}
#TargetApi(29)
private fun Activity.checkBackgroundLocationPermission() {
if (isFineAndBackgroundLocationPermissionsGranted) return
val shouldShowBackgroundPermissionRationale = ActivityCompat
.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
if (shouldShowBackgroundPermissionRationale) {
presentAlertDialog(
R.string.dialog_background_location_rationale_title,
R.string.dialog_background_location_rationale_description,
R.string.yes,
) {
requestFineLocationAndBackground()
}
} else {
requestFineLocationAndBackground()
}
}
fun checkLocationPermissions(activity: Activity, action: () -> Unit) = with(activity) {
if (isFineAndBackgroundLocationPermissionsGranted) {
action()
return
}
checkFineLocationPermission()
}
fun onRequestPermissionsResult(
activity: Activity,
requestCode: Int,
action: () -> Unit
) = with(activity) {
when (requestCode) {
REQUEST_CODE_FOREGROUND -> {
if (!isFineLocationPermissionGranted) {
checkFineLocationPermission()
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
checkBackgroundLocationPermission()
} else {
action()
}
}
REQUEST_CODE_FOREGROUND_AND_BACKGROUND -> {
if (!isFineLocationPermissionGranted) {
checkFineLocationPermission()
return
}
if (isBackgroundPermissionGranted) {
action()
} else {
checkBackgroundLocationPermission()
}
}
}
}
}
Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
LocationPermissionUtil.checkLocationPermissions(this, this::onLocationPermissionsGranted)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
LocationPermissionUtil.onRequestPermissionsResult(
this,
requestCode,
this::onLocationPermissionsGranted
)
}
private fun onLocationPermissionsGranted() {
Toast.makeText(
this,
"Background location permitted, starting location tracking...",
Toast.LENGTH_LONG
).show()
}
}
check this code from MainActivity
// Check location permission is granted - if it is, start
// the service, otherwise request the permission
fun checkOrAskLocationPermission(callback: () -> Unit) {
// Check GPS is enabled
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "Please enable location services", Toast.LENGTH_SHORT).show()
buildAlertMessageNoGps(this)
return
}
// Check location permission is granted - if it is, start
// the service, otherwise request the permission
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
if (permission == PackageManager.PERMISSION_GRANTED) {
callback.invoke()
} else {
// callback will be inside the activity's onRequestPermissionsResult(
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSIONS_REQUEST
)
}
}
plus
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
// Permission ok. Do work.
}
}
}
plus
fun buildAlertMessageNoGps(context: Context) {
val builder = AlertDialog.Builder(context);
builder.setMessage("Your GPS is disabled. Do you want to enable it?")
.setCancelable(false)
.setPositiveButton("Yes") { _, _ -> context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) }
.setNegativeButton("No") { dialog, _ -> dialog.cancel(); }
val alert = builder.create();
alert.show();
}
usage
checkOrAskLocationPermission() {
// Permission ok. Do work.
}
This code work for me. I also handled case "Never Ask Me"
In AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
In build.gradle (Module: app)
dependencies {
....
implementation "com.google.android.gms:play-services-location:16.0.0"
}
This is CurrentLocationManager.kt
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.CountDownTimer
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.util.Log
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.LocationSettingsRequest
import com.google.android.gms.location.LocationSettingsStatusCodes
import java.lang.ref.WeakReference
object CurrentLocationManager : LocationListener {
const val REQUEST_CODE_ACCESS_LOCATION = 123
fun checkLocationPermission(activity: Activity) {
if (ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE_ACCESS_LOCATION
)
} else {
Thread(Runnable {
// Moves the current Thread into the background
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)
//
requestLocationUpdates(activity)
}).start()
}
}
/**
* be used in HomeActivity.
*/
const val REQUEST_CHECK_SETTINGS = 55
/**
* The number of millis in the future from the call to start().
* until the countdown is done and onFinish() is called.
*
*
* It is also the interval along the way to receive onTick(long) callbacks.
*/
private const val TWENTY_SECS: Long = 20000
/**
* Timer to get location from history when requestLocationUpdates don't return result.
*/
private var mCountDownTimer: CountDownTimer? = null
/**
* WeakReference of current activity.
*/
private var mWeakReferenceActivity: WeakReference<Activity>? = null
/**
* user's location.
*/
var currentLocation: Location? = null
#Synchronized
fun requestLocationUpdates(activity: Activity) {
if (mWeakReferenceActivity == null) {
mWeakReferenceActivity = WeakReference(activity)
} else {
mWeakReferenceActivity?.clear()
mWeakReferenceActivity = WeakReference(activity)
}
//create location request: https://developer.android.com/training/location/change-location-settings.html#prompt
val mLocationRequest = LocationRequest()
// Which your app prefers to receive location updates. Note that the location updates may be
// faster than this rate, or slower than this rate, or there may be no updates at all
// (if the device has no connectivity)
mLocationRequest.interval = 20000
//This method sets the fastest rate in milliseconds at which your app can handle location updates.
// You need to set this rate because other apps also affect the rate at which updates are sent
mLocationRequest.fastestInterval = 10000
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
//Get Current Location Settings
val builder = LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest)
//Next check whether the current location settings are satisfied
val client = LocationServices.getSettingsClient(activity)
val task = client.checkLocationSettings(builder.build())
//Prompt the User to Change Location Settings
task.addOnSuccessListener(activity) {
Log.d("CurrentLocationManager", "OnSuccessListener")
// All location settings are satisfied. The client can initialize location requests here.
// If it's failed, the result after user updated setting is sent to onActivityResult of HomeActivity.
val activity1 = mWeakReferenceActivity?.get()
if (activity1 != null) {
startRequestLocationUpdate(activity1.applicationContext)
}
}
task.addOnFailureListener(activity) { e ->
Log.d("CurrentLocationManager", "addOnFailureListener")
val statusCode = (e as ApiException).statusCode
when (statusCode) {
CommonStatusCodes.RESOLUTION_REQUIRED ->
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
val activity1 = mWeakReferenceActivity?.get()
if (activity1 != null) {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
val resolvable = e as ResolvableApiException
resolvable.startResolutionForResult(
activity1, REQUEST_CHECK_SETTINGS
)
}
} catch (sendEx: IntentSender.SendIntentException) {
// Ignore the error.
sendEx.printStackTrace()
}
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.
}
}
}
}
fun startRequestLocationUpdate(appContext: Context) {
val mLocationManager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (ActivityCompat.checkSelfPermission(
appContext.applicationContext,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
//Utilities.showProgressDialog(mWeakReferenceActivity.get());
if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 10000, 0f, this
)
} else {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 10000, 0f, this
)
}
}
/*Timer to call getLastKnownLocation() when requestLocationUpdates don 't return result*/
countDownUpdateLocation()
}
override fun onLocationChanged(location: Location?) {
if (location != null) {
stopRequestLocationUpdates()
currentLocation = location
}
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
}
override fun onProviderEnabled(provider: String) {
}
override fun onProviderDisabled(provider: String) {
}
/**
* Init CountDownTimer to to get location from history when requestLocationUpdates don't return result.
*/
#Synchronized
private fun countDownUpdateLocation() {
mCountDownTimer?.cancel()
mCountDownTimer = object : CountDownTimer(TWENTY_SECS, TWENTY_SECS) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
if (mWeakReferenceActivity != null) {
val activity = mWeakReferenceActivity?.get()
if (activity != null && ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
val location = (activity.applicationContext
.getSystemService(Context.LOCATION_SERVICE) as LocationManager)
.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
stopRequestLocationUpdates()
onLocationChanged(location)
} else {
stopRequestLocationUpdates()
}
} else {
mCountDownTimer?.cancel()
mCountDownTimer = null
}
}
}.start()
}
/**
* The method must be called in onDestroy() of activity to
* removeUpdateLocation and cancel CountDownTimer.
*/
fun stopRequestLocationUpdates() {
val activity = mWeakReferenceActivity?.get()
if (activity != null) {
/*if (ActivityCompat.checkSelfPermission(activity,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {*/
(activity.applicationContext
.getSystemService(Context.LOCATION_SERVICE) as LocationManager).removeUpdates(this)
/*}*/
}
mCountDownTimer?.cancel()
mCountDownTimer = null
}
}
In MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
}
override fun onDestroy() {
CurrentLocationManager.stopRequestLocationUpdates()
super.onDestroy()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == CurrentLocationManager.REQUEST_CODE_ACCESS_LOCATION) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
//denied
val builder = AlertDialog.Builder(this)
builder.setMessage("We need permission to use your location for the purpose of finding friends near you.")
.setTitle("Device Location Required")
.setIcon(com.eswapp.R.drawable.ic_info)
.setPositiveButton("OK") { _, _ ->
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
//only deny
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
} else {
//never ask again
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivityForResult(intent, CurrentLocationManager.REQUEST_CHECK_SETTINGS)
}
}
.setNegativeButton("Ask Me Later") { _, _ ->
}
// Create the AlertDialog object and return it
val dialog = builder.create()
dialog.show()
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
CurrentLocationManager.requestLocationUpdates(this)
}
}
}
//Forward Login result to the CallBackManager in OnActivityResult()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
//case 1. After you allow the app access device location, Another dialog will be displayed to request you to turn on device location
//case 2. Or You chosen Never Ask Again, you open device Setting and enable location permission
CurrentLocationManager.REQUEST_CHECK_SETTINGS -> when (resultCode) {
RESULT_OK -> {
Log.d("REQUEST_CHECK_SETTINGS", "RESULT_OK")
//case 1. You choose OK
CurrentLocationManager.startRequestLocationUpdate(applicationContext)
}
RESULT_CANCELED -> {
Log.d("REQUEST_CHECK_SETTINGS", "RESULT_CANCELED")
//case 1. You choose NO THANKS
//CurrentLocationManager.requestLocationUpdates(this)
//case 2. In device Setting screen: user can enable or not enable location permission,
// so when user back to this activity, we should re-call checkLocationPermission()
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
}
else -> {
//do nothing
}
}
else -> {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
After having it defined in your manifest file, a friendlier alternative to the native solution would be using Aaper: https://github.com/LikeTheSalad/aaper like so:
#EnsurePermissions(permissions = [Manifest.permission.ACCESS_FINE_LOCATION])
private fun scanForLocation() {
// Your code that needs the location permission granted.
}
Disclaimer, I'm the creator of Aaper.
Looking for a simpler code? Try this!
if (ContextCompat.checkSelfPermission(LoginActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(LoginActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CALL);
}
Also, don't forget to ask for permissions obviously
In the manifest file I added permissions coarse and fine, and when I run on device with Android 6, nothing happens! I try
everything but no way to get location updates...
What am I doing wrong?
public class MainActivity extends AppCompatActivity implements LocationListener {
LocationManager locationManager;
String provider;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
provider = locationManager.getBestProvider(new Criteria(), false);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
Log.i("Location Info", "Location achieved!");
} else {
Log.i("Location Info", "No location :(");
}
}
#Override
protected void onResume() {
super.onResume();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
#Override
protected void onPause() {
super.onPause();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
locationManager.removeUpdates(this);
}
#Override
public void onLocationChanged(Location location) {
Double lat = location.getLatitude();
Double lng = location.getLongitude();
Log.i("Location info: Lat", lat.toString());
Log.i("Location info: Lng", lng.toString());
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
public void getLocation(View view) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
}
Location location = locationManager.getLastKnownLocation(provider);
onLocationChanged(location);
}
}
You need to actually request the Location permission at runtime (notice the comments in your code stating this).
Updated with Kotlin and background location for API 31 (Android 12):
Starting with API 30 background location must be requested separately.
This example is using targetSdk 31 and compileSdk 31.
Note that it's possible to bundle the background location request along with the main location request on API 29, however to do that you would need to maintain three separate code paths.
It's easier to just break it out to separate requests for 29 and above.
Be sure to include the latest location services in the app level gradle (18.0.0 at the time of writing):
implementation "com.google.android.gms:play-services-location:18.0.0"
Include the location permissions in the manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
This is a simplified example that does handle most cases, but in a simplified way. In the case where a user chooses "Don't ask again", on the next app launch it will open up the settings for the user to manually enable the permission.
Full activity code:
import android.Manifest
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
class MainActivity : AppCompatActivity() {
private var fusedLocationProvider: FusedLocationProviderClient? = null
private val locationRequest: LocationRequest = LocationRequest.create().apply {
interval = 30
fastestInterval = 10
priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
maxWaitTime = 60
}
private var locationCallback: LocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val locationList = locationResult.locations
if (locationList.isNotEmpty()) {
//The last location in the list is the newest
val location = locationList.last()
Toast.makeText(
this#MainActivity,
"Got Location: " + location.toString(),
Toast.LENGTH_LONG
)
.show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fusedLocationProvider = LocationServices.getFusedLocationProviderClient(this)
checkLocationPermission()
}
override fun onResume() {
super.onResume()
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}
override fun onPause() {
super.onPause()
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
== PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.removeLocationUpdates(locationCallback)
}
}
private fun checkLocationPermission() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
// 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.
AlertDialog.Builder(this)
.setTitle("Location Permission Needed")
.setMessage("This app needs the Location permission, please accept to use location functionality")
.setPositiveButton(
"OK"
) { _, _ ->
//Prompt the user once explanation has been shown
requestLocationPermission()
}
.create()
.show()
} else {
// No explanation needed, we can request the permission.
requestLocationPermission()
}
} else {
checkBackgroundLocation()
}
}
private fun checkBackgroundLocation() {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
requestBackgroundLocationPermission()
}
}
private fun requestLocationPermission() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
),
MY_PERMISSIONS_REQUEST_LOCATION
)
}
private fun requestBackgroundLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION
)
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
MY_PERMISSIONS_REQUEST_LOCATION
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_LOCATION -> {
// If request is cancelled, the result arrays are empty.
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,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
// Now check background location
checkBackgroundLocation()
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
// Check if we are in a state where the user has denied the permission and
// selected Don't ask again
if (!ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
startActivity(
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", this.packageName, null),
),
)
}
}
return
}
MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION -> {
// If request is cancelled, the result arrays are empty.
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,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationProvider?.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
Toast.makeText(
this,
"Granted Background Location Permission",
Toast.LENGTH_LONG
).show()
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show()
}
return
}
}
}
companion object {
private const val MY_PERMISSIONS_REQUEST_LOCATION = 99
private const val MY_PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 66
}
}
On Android 10 (API 29) it will give the user the choice to grant background location after the initial location request:
On Android 12 (API 31) it will do the same, but the interface is different:
Original Answer in Java:
Here is tested and working code to request the Location permission.
Put this code in the Activity:
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// 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.
new AlertDialog.Builder(this)
.setTitle(R.string.title_location_permission)
.setMessage(R.string.text_location_permission)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// location-related task you need to do.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
//Request location updates:
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
}
}
Then call the checkLocationPermission() method in onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//.........
checkLocationPermission();
}
You can then use onResume() and onPause() exactly as it is in the question.
Here is a condensed version that is a bit more clean:
#Override
protected void onResume() {
super.onResume();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.requestLocationUpdates(provider, 400, 1, this);
}
}
#Override
protected void onPause() {
super.onPause();
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
locationManager.removeUpdates(this);
}
}
Google has created a library for easy Permissions management. Its called EasyPermissions
Here is a simple example on requesting Location permission using this library.
public class MainActivity extends AppCompatActivity {
private final int REQUEST_LOCATION_PERMISSION = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestLocationPermission();
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
#AfterPermissionGranted(REQUEST_LOCATION_PERMISSION)
public void requestLocationPermission() {
String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION};
if(EasyPermissions.hasPermissions(this, perms)) {
Toast.makeText(this, "Permission already granted", Toast.LENGTH_SHORT).show();
}
else {
EasyPermissions.requestPermissions(this, "Please grant the location permission", REQUEST_LOCATION_PERMISSION, perms);
}
}
}
#AfterPermissionsGranted(REQUEST_CODE) is used to indicate the method that needs to be executed after a permission request with the request code REQUEST_CODE has been granted.
This above case, the method requestLocationPermission() method is called if the user grants the permission to access location services. So, that method acts as both a callback and a method to request the permissions.
You can implement separate callbacks for permission granted and permission denied as well. It is explained in the github page.
Location permission privacy change in Android 10 or Android Q.
We have to define additional ACCESS_BACKGROUND_LOCATION permission if user wants to access their current location in background so user needs to granted permission runtime also in requestPermission()
If we are using lower than Android 10 device then ACCESS_BACKGROUND_LOCATION permission allow automatically with ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission
This tabular format might be easy to understand what if we don't specify ACCESS_BACKGROUND_LOCATION in manifest file.
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> // here we defined ACCESS_BACKGROUND_LOCATION for Android 10 device
MainActivity.java
Call checkRunTimePermission() in onCreate() or onResume()
public void checkRunTimePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
} else {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION},
10);
}
} else {
gpsTracker = new GPSTracker(context); //GPSTracker is class that is used for retrieve user current location
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 10) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.ACCESS_FINE_LOCATION)) {
// If User Checked 'Don't Show Again' checkbox for runtime permission, then navigate user to Settings
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle("Permission Required");
dialog.setCancelable(false);
dialog.setMessage("You have to Allow permission to access user location");
dialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent i = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package",
context.getPackageName(), null));
//i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(i, 1001);
}
});
AlertDialog alertDialog = dialog.create();
alertDialog.show();
}
//code for deny
}
}
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
super.startActivityForResult(intent, requestCode);
switch (requestCode) {
case 1001:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
gpsTracker = new GPSTracker(context);
if (gpsTracker.canGetLocation()) {
latitude = gpsTracker.getLatitude();
longitude = gpsTracker.getLongitude();
}
} else {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION},10);
}
}
break;
default:
break;
}
}
build.gradle (app level)
android {
compileSdkVersion 29 //should be >= 29
buildToolsVersion "29.0.2"
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "com.example.runtimepermission"
minSdkVersion 21
targetSdkVersion 29 //should be >= 29
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
}
Here you can find GPSTracker.java file code
Below is my solution to requesting foreground and background location permissions on 28 and below, 29, and 30. The differences between the APIs are subtle but important.
API 28 and below, the system treats foreground and background location permissions as the same. If you grant location permissions, then the application is granted both implicitly.
API 29, you can request both foreground and background permissions at the same time.
API 30, you must request foreground location permissions and then only request background location permissions after the foreground location permissions have been granted. If you ask for both foreground and background permissions at the same time, then the system will ignore the request. Another difference is that the user must allow background location permissions within the application location permission settings rather than through a system dialog.
The solution below only starts the specified action (e.g. background location tracking) only after the user has accepted both foreground and background location tracking:
LocationPermissionUtil.kt
private const val REQUEST_CODE_FOREGROUND = 1
private const val REQUEST_CODE_FOREGROUND_AND_BACKGROUND = 2
object LocationPermissionUtil {
private fun Context.isPermissionGranted(permission: String): Boolean = ActivityCompat
.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
private val Context.isFineLocationPermissionGranted
get() = isPermissionGranted(
Manifest.permission.ACCESS_FINE_LOCATION
)
private val Context.isBackgroundPermissionGranted
get() = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
) == PackageManager.PERMISSION_GRANTED
else -> isFineLocationPermissionGranted
}
private val Context.isFineAndBackgroundLocationPermissionsGranted
get() = isFineLocationPermissionGranted && isBackgroundPermissionGranted
private fun Activity.checkFineLocationPermission() {
if (isFineLocationPermissionGranted) return
val shouldShowFineLocationPermissionRationale = ActivityCompat
.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
if (shouldShowFineLocationPermissionRationale) {
presentAlertDialog(
R.string.dialog_fine_location_rationale_title,
R.string.dialog_fine_location_rationale_description,
R.string.yes,
) {
requestLocationPermissions()
}
} else {
requestLocationPermissions()
}
}
private fun Activity.requestLocationPermissions() =
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
requestFineLocationAndBackground()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE_FOREGROUND
)
}
#TargetApi(29)
private fun Activity.requestFineLocationAndBackground() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
REQUEST_CODE_FOREGROUND_AND_BACKGROUND
)
}
#TargetApi(29)
private fun Activity.checkBackgroundLocationPermission() {
if (isFineAndBackgroundLocationPermissionsGranted) return
val shouldShowBackgroundPermissionRationale = ActivityCompat
.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
if (shouldShowBackgroundPermissionRationale) {
presentAlertDialog(
R.string.dialog_background_location_rationale_title,
R.string.dialog_background_location_rationale_description,
R.string.yes,
) {
requestFineLocationAndBackground()
}
} else {
requestFineLocationAndBackground()
}
}
fun checkLocationPermissions(activity: Activity, action: () -> Unit) = with(activity) {
if (isFineAndBackgroundLocationPermissionsGranted) {
action()
return
}
checkFineLocationPermission()
}
fun onRequestPermissionsResult(
activity: Activity,
requestCode: Int,
action: () -> Unit
) = with(activity) {
when (requestCode) {
REQUEST_CODE_FOREGROUND -> {
if (!isFineLocationPermissionGranted) {
checkFineLocationPermission()
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
checkBackgroundLocationPermission()
} else {
action()
}
}
REQUEST_CODE_FOREGROUND_AND_BACKGROUND -> {
if (!isFineLocationPermissionGranted) {
checkFineLocationPermission()
return
}
if (isBackgroundPermissionGranted) {
action()
} else {
checkBackgroundLocationPermission()
}
}
}
}
}
Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
LocationPermissionUtil.checkLocationPermissions(this, this::onLocationPermissionsGranted)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
LocationPermissionUtil.onRequestPermissionsResult(
this,
requestCode,
this::onLocationPermissionsGranted
)
}
private fun onLocationPermissionsGranted() {
Toast.makeText(
this,
"Background location permitted, starting location tracking...",
Toast.LENGTH_LONG
).show()
}
}
check this code from MainActivity
// Check location permission is granted - if it is, start
// the service, otherwise request the permission
fun checkOrAskLocationPermission(callback: () -> Unit) {
// Check GPS is enabled
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "Please enable location services", Toast.LENGTH_SHORT).show()
buildAlertMessageNoGps(this)
return
}
// Check location permission is granted - if it is, start
// the service, otherwise request the permission
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
if (permission == PackageManager.PERMISSION_GRANTED) {
callback.invoke()
} else {
// callback will be inside the activity's onRequestPermissionsResult(
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSIONS_REQUEST
)
}
}
plus
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_REQUEST) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
// Permission ok. Do work.
}
}
}
plus
fun buildAlertMessageNoGps(context: Context) {
val builder = AlertDialog.Builder(context);
builder.setMessage("Your GPS is disabled. Do you want to enable it?")
.setCancelable(false)
.setPositiveButton("Yes") { _, _ -> context.startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) }
.setNegativeButton("No") { dialog, _ -> dialog.cancel(); }
val alert = builder.create();
alert.show();
}
usage
checkOrAskLocationPermission() {
// Permission ok. Do work.
}
This code work for me. I also handled case "Never Ask Me"
In AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
In build.gradle (Module: app)
dependencies {
....
implementation "com.google.android.gms:play-services-location:16.0.0"
}
This is CurrentLocationManager.kt
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.CountDownTimer
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.util.Log
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.LocationSettingsRequest
import com.google.android.gms.location.LocationSettingsStatusCodes
import java.lang.ref.WeakReference
object CurrentLocationManager : LocationListener {
const val REQUEST_CODE_ACCESS_LOCATION = 123
fun checkLocationPermission(activity: Activity) {
if (ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_CODE_ACCESS_LOCATION
)
} else {
Thread(Runnable {
// Moves the current Thread into the background
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)
//
requestLocationUpdates(activity)
}).start()
}
}
/**
* be used in HomeActivity.
*/
const val REQUEST_CHECK_SETTINGS = 55
/**
* The number of millis in the future from the call to start().
* until the countdown is done and onFinish() is called.
*
*
* It is also the interval along the way to receive onTick(long) callbacks.
*/
private const val TWENTY_SECS: Long = 20000
/**
* Timer to get location from history when requestLocationUpdates don't return result.
*/
private var mCountDownTimer: CountDownTimer? = null
/**
* WeakReference of current activity.
*/
private var mWeakReferenceActivity: WeakReference<Activity>? = null
/**
* user's location.
*/
var currentLocation: Location? = null
#Synchronized
fun requestLocationUpdates(activity: Activity) {
if (mWeakReferenceActivity == null) {
mWeakReferenceActivity = WeakReference(activity)
} else {
mWeakReferenceActivity?.clear()
mWeakReferenceActivity = WeakReference(activity)
}
//create location request: https://developer.android.com/training/location/change-location-settings.html#prompt
val mLocationRequest = LocationRequest()
// Which your app prefers to receive location updates. Note that the location updates may be
// faster than this rate, or slower than this rate, or there may be no updates at all
// (if the device has no connectivity)
mLocationRequest.interval = 20000
//This method sets the fastest rate in milliseconds at which your app can handle location updates.
// You need to set this rate because other apps also affect the rate at which updates are sent
mLocationRequest.fastestInterval = 10000
mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
//Get Current Location Settings
val builder = LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest)
//Next check whether the current location settings are satisfied
val client = LocationServices.getSettingsClient(activity)
val task = client.checkLocationSettings(builder.build())
//Prompt the User to Change Location Settings
task.addOnSuccessListener(activity) {
Log.d("CurrentLocationManager", "OnSuccessListener")
// All location settings are satisfied. The client can initialize location requests here.
// If it's failed, the result after user updated setting is sent to onActivityResult of HomeActivity.
val activity1 = mWeakReferenceActivity?.get()
if (activity1 != null) {
startRequestLocationUpdate(activity1.applicationContext)
}
}
task.addOnFailureListener(activity) { e ->
Log.d("CurrentLocationManager", "addOnFailureListener")
val statusCode = (e as ApiException).statusCode
when (statusCode) {
CommonStatusCodes.RESOLUTION_REQUIRED ->
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
val activity1 = mWeakReferenceActivity?.get()
if (activity1 != null) {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
val resolvable = e as ResolvableApiException
resolvable.startResolutionForResult(
activity1, REQUEST_CHECK_SETTINGS
)
}
} catch (sendEx: IntentSender.SendIntentException) {
// Ignore the error.
sendEx.printStackTrace()
}
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.
}
}
}
}
fun startRequestLocationUpdate(appContext: Context) {
val mLocationManager = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (ActivityCompat.checkSelfPermission(
appContext.applicationContext,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
//Utilities.showProgressDialog(mWeakReferenceActivity.get());
if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 10000, 0f, this
)
} else {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 10000, 0f, this
)
}
}
/*Timer to call getLastKnownLocation() when requestLocationUpdates don 't return result*/
countDownUpdateLocation()
}
override fun onLocationChanged(location: Location?) {
if (location != null) {
stopRequestLocationUpdates()
currentLocation = location
}
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
}
override fun onProviderEnabled(provider: String) {
}
override fun onProviderDisabled(provider: String) {
}
/**
* Init CountDownTimer to to get location from history when requestLocationUpdates don't return result.
*/
#Synchronized
private fun countDownUpdateLocation() {
mCountDownTimer?.cancel()
mCountDownTimer = object : CountDownTimer(TWENTY_SECS, TWENTY_SECS) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
if (mWeakReferenceActivity != null) {
val activity = mWeakReferenceActivity?.get()
if (activity != null && ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
val location = (activity.applicationContext
.getSystemService(Context.LOCATION_SERVICE) as LocationManager)
.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
stopRequestLocationUpdates()
onLocationChanged(location)
} else {
stopRequestLocationUpdates()
}
} else {
mCountDownTimer?.cancel()
mCountDownTimer = null
}
}
}.start()
}
/**
* The method must be called in onDestroy() of activity to
* removeUpdateLocation and cancel CountDownTimer.
*/
fun stopRequestLocationUpdates() {
val activity = mWeakReferenceActivity?.get()
if (activity != null) {
/*if (ActivityCompat.checkSelfPermission(activity,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {*/
(activity.applicationContext
.getSystemService(Context.LOCATION_SERVICE) as LocationManager).removeUpdates(this)
/*}*/
}
mCountDownTimer?.cancel()
mCountDownTimer = null
}
}
In MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
}
override fun onDestroy() {
CurrentLocationManager.stopRequestLocationUpdates()
super.onDestroy()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == CurrentLocationManager.REQUEST_CODE_ACCESS_LOCATION) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
//denied
val builder = AlertDialog.Builder(this)
builder.setMessage("We need permission to use your location for the purpose of finding friends near you.")
.setTitle("Device Location Required")
.setIcon(com.eswapp.R.drawable.ic_info)
.setPositiveButton("OK") { _, _ ->
if (ActivityCompat.shouldShowRequestPermissionRationale(
this,
Manifest.permission.ACCESS_FINE_LOCATION
)
) {
//only deny
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
} else {
//never ask again
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivityForResult(intent, CurrentLocationManager.REQUEST_CHECK_SETTINGS)
}
}
.setNegativeButton("Ask Me Later") { _, _ ->
}
// Create the AlertDialog object and return it
val dialog = builder.create()
dialog.show()
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
CurrentLocationManager.requestLocationUpdates(this)
}
}
}
//Forward Login result to the CallBackManager in OnActivityResult()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
//case 1. After you allow the app access device location, Another dialog will be displayed to request you to turn on device location
//case 2. Or You chosen Never Ask Again, you open device Setting and enable location permission
CurrentLocationManager.REQUEST_CHECK_SETTINGS -> when (resultCode) {
RESULT_OK -> {
Log.d("REQUEST_CHECK_SETTINGS", "RESULT_OK")
//case 1. You choose OK
CurrentLocationManager.startRequestLocationUpdate(applicationContext)
}
RESULT_CANCELED -> {
Log.d("REQUEST_CHECK_SETTINGS", "RESULT_CANCELED")
//case 1. You choose NO THANKS
//CurrentLocationManager.requestLocationUpdates(this)
//case 2. In device Setting screen: user can enable or not enable location permission,
// so when user back to this activity, we should re-call checkLocationPermission()
CurrentLocationManager.checkLocationPermission(this#LoginActivity)
}
else -> {
//do nothing
}
}
else -> {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
After having it defined in your manifest file, a friendlier alternative to the native solution would be using Aaper: https://github.com/LikeTheSalad/aaper like so:
#EnsurePermissions(permissions = [Manifest.permission.ACCESS_FINE_LOCATION])
private fun scanForLocation() {
// Your code that needs the location permission granted.
}
Disclaimer, I'm the creator of Aaper.
Looking for a simpler code? Try this!
if (ContextCompat.checkSelfPermission(LoginActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(LoginActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CALL);
}
Also, don't forget to ask for permissions obviously
I am trying to make a phone call from Android, and I've set run time permissions as well. And it asks whether to allow making phone calls. But when I press allow, the app crashes:
This is how I implemented it:
private static final int REQUEST_PHONE_CALL = 1;
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},REQUEST_PHONE_CALL);
}
else
{
startActivity(intent);
}
}
else
{
startActivity(intent);
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_PHONE_CALL: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startActivity(intent);
}
else
{
}
return;
}
}
}
This is what I obtain in logcat:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=#android:requestPermissions:,
request=1, result=-1, data=Intent { act=android.content.pm.action.REQUEST_PERMISSIONS (has extras) }}
to activity {com.devpost.airway/com.devpost.airway.activities.MainActivity}:
java.lang.NullPointerException: Attempt to invoke virtual method
'java.lang.String android.content.Intent.toString()' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3733)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3776)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1412)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method
'java.lang.String android.content.Intent.toString()' on a null object reference
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1485)
at android.app.Activity.startActivityForResult(Activity.java:3930)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:48)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:75)
at android.app.Activity.startActivityForResult(Activity.java:3890)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:856)
at android.app.Activity.startActivity(Activity.java:4213)
at android.app.Activity.startActivity(Activity.java:4181)
at com.devpost.airway.activities.MainActivity.onRequestPermissionsResult(MainActivity.java:140)
at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6582)
at android.app.Activity.dispatchActivityResult(Activity.java:6460)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3729)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3776)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1412)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5461)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
What is possibly causing this?
I would better suggest to use ACTION_DIAL rather than ACTION_CALL while constructing Intent to call a particular number . Using ACTION_DIAL , you will need no call permissions in your app, as ACTION_DIAL opens the dialer with the number already entered, and further allows the user to decide whether to actually make the call or modify the phone number before calling or not call at all.
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + "Your Phone_number"));// Initiates the Intent
startActivity(intent);
The stack trace seems to indicate that your permissions flow is working ok, but the call to startActivity from onRequestPermissionsResult() is crashing. Is the Intent you're passing to startActivity set correctly? I can't see it being set in that part of the code.
Note also that ContextCompat.checkSelfPermission handles the SDK version checking on your behalf, so you should be able to use
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE},REQUEST_PHONE_CALL);
}
else
{
startActivity(intent);
}
by itself, without the wrapping SDK version check code.
You need to create your Intent in onRequestPermissionsResult
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_PHONE_CALL: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));
startActivity(intent);
}
else
{
}
return;
}
}
}
add new method
public static boolean hasPermissions(Context context, String... permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
add this in Global
int PERMISSION_ALL = 1;
String[] PERMISSIONS = {Manifest.permission.READ_CONTACTS, Manifest.permission.CALL_PHONE};
and write below code in onCreate
if(!hasPermissions(this, PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
}
It is better to not use phone_call permission for that action. Google Play console will alert you that you should avoid asking for unnecessary permission from the user. This is a better approach:
/**
* Make a phone call. Send to the phone app
*
* #param phoneNumber the phone number to call
*/
private fun performPhoneCall(phoneNumber: String) {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:$phoneNumber")
try {
context.startActivity(intent)
} catch (ex: Exception) {
displayErrorMessage()
}
}
Change your onRequestPermissionsResult to below, you basically need to create the intent first and then call it on permission granted. That's where you are doing it wrong.
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_PHONE_CALL : {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + "+918511812660"));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
startActivity(intent);
}
}
}
}
}
private void makePhoneCall() {
String number = items;
if (number.trim().length() > 0) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL);
} else {
String dial = "tel:" + number;
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse(dial)));
}
} else {
Toast.makeText(MainActivity.this, "Enter Phone Number", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == REQUEST_CALL) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
makePhoneCall();
} else {
Toast.makeText(this, "Permission DENIED", Toast.LENGTH_SHORT).show();
}
}
}
I am trying to create an app. App asking every time location Access in Marsh Mallow, always when we open the app. Please suggested the best Process for solve my problem. using following code-
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
//for marshmallow
final int LOCATION_PERMISSION_REQUEST_CODE = 100;
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION},
LOCATION_PERMISSION_REQUEST_CODE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
mMap.setMyLocationEnabled(true);
buildGoogleApiClient();
mGoogleApiClient.connect();
}
thanks.
Try writting in if else, if permission is not given ask for it or else do what you want to do.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(checkSelfPermission(Manifest.permission.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.Manifest.permission.ACCESS_FINE_LOCATION},
LOCATION_PERMISSION_REQUEST_CODE);
}
} else {
// do your stuff
}
and also override onRequestPermissionsResult and apply your code afer you have granted permissions.
Please reflect your code yourself. Think about the order of requesting and checking the permission. You should check the permission before you're asking for it. This makes sure to prevent asking if the permission was already granted...
Have a look at the example provided by Google and consider adapting this example for your needs:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) {
// Show an expanation 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(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
Additionally you should overwrite the callback method onRequestPermissionsResult to handle the user's grantResults.
// Callback with the request from calling requestPermissions(...)
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String permissions[],
#NonNull int[] grantResults) {
// Make sure it's our original READ_CONTACTS request
if (requestCode == READ_CONTACTS_PERMISSIONS_REQUEST) {
if (grantResults.length == 1 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Read Contacts permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Read Contacts permission denied", Toast.LENGTH_SHORT).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Edit:
This is thecode which you should change
ActivityCompat.requestPermissions(this, new String[] {android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION},
LOCATION_PERMISSION_REQUEST_CODE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
For further reading have a look at the article Understanding App Permissions