Related
I have a class which checks the location permission and show the dialog if not granted and its working fine if I run it separately but when I try to use it in another class it gave me the exception
"Attempt to invoke virtual method
'android.app.ActivityThread$ApplicationThread
android.app.ActivityThread.getApplicationThread()' on a null object
reference"
This is my class :
public class LocSettingActivity extends AppCompatActivity {
private static final int REQUEST_CHECK_SETTINGS = 0x1;
private static GoogleApiClient mGoogleApiClient;
private static final int ACCESS_FINE_LOCATION_INTENT_ID = 3;
private static final String BROADCAST_ACTION = "android.location.PROVIDERS_CHANGED";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loc_setting);
initGoogleAPIClient(this);//Init Google API Client
}
/* Initiate Google API Client */
private void initGoogleAPIClient(Context context) {
//Without Google API Client Auto Location Dialog will not work
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
/* Check Location Permission for Marshmallow Devices */
public void checkPermissions(Context context) {
initGoogleAPIClient(context);
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(context,
android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED)
requestLocationPermission();
else
showSettingDialog();
} else
showSettingDialog();
}
/* Show Popup to access User Permission */
public void requestLocationPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(LocSettingActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION)) {
ActivityCompat.requestPermissions(LocSettingActivity.this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
ACCESS_FINE_LOCATION_INTENT_ID);
} else {
ActivityCompat.requestPermissions(LocSettingActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
ACCESS_FINE_LOCATION_INTENT_ID);
}
}
/* Show Location Access Dialog */
public void showSettingDialog() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);//Setting priotity of Location request to high
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);//5 sec Time interval for location update
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true); //this is the key ingredient to show dialog always when GPS is off
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
// updateGPSStatus("GPS is Enabled in your device");
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(LocSettingActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
// Ignore the error.
}
break;
case 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.
break;
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
// Check for the integer request code originally supplied to startResolutionForResult().
case REQUEST_CHECK_SETTINGS:
switch (resultCode) {
case RESULT_OK:
Log.e("Settings", "Result OK");
// updateGPSStatus("GPS is Enabled in your device");
//startLocationUpdates();
break;
case RESULT_CANCELED:
Log.e("Settings", "Result Cancel");
// updateGPSStatus("GPS is Disabled in your device");
break;
}
break;
}
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(gpsLocationReceiver, new IntentFilter(BROADCAST_ACTION));//Register broadcast receiver to check the status of GPS
checkPermissions(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
//Unregister receiver on destroy
if (gpsLocationReceiver != null)
unregisterReceiver(gpsLocationReceiver);
}
//Run on UI
public Runnable sendUpdatesToUI = new Runnable() {
public void run() {
showSettingDialog();
}
};
// Broadcast receiver to check status of GPS
private BroadcastReceiver gpsLocationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try {
//If Action is Location
if (intent.getAction().matches(BROADCAST_ACTION)) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//Check if GPS is turned ON or OFF
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Log.e("About GPS", "GPS is Enabled in your device");
// updateGPSStatus("GPS is Enabled in your device");
} else {
//If GPS turned OFF show Location Dialog
new Handler().postDelayed(sendUpdatesToUI, 10);
// showSettingDialog();
// updateGPSStatus("GPS is Disabled in your device");
checkPermissions(context);
Log.e("About GPS", "GPS is Disabled in your device");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
//Method to update GPS status text
private void updateGPSStatus(Context context,String status) {
Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
}
/* On Request permission method to check the permisison is granted or not for Marshmallow+ Devices */
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
try {
switch (requestCode) {
case ACCESS_FINE_LOCATION_INTENT_ID: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//If permission granted show location dialog if APIClient is not null
if (mGoogleApiClient == null) {
initGoogleAPIClient(this);
showSettingDialog();
} else
showSettingDialog();
} else {
// updateGPSStatus("Location Permission denied.");
Toast.makeText(LocSettingActivity.this, "Location Permission denied.", Toast.LENGTH_SHORT).show();
simpleAlertGps();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void simpleAlertGps() {
try {
android.app.AlertDialog alertDialog = new android.app.AlertDialog.Builder(this).create();
alertDialog.setTitle("");
alertDialog.setMessage("Your Gps Must Be On For Better Result");
alertDialog.setButton(android.app.AlertDialog.BUTTON_POSITIVE, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
/*Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);*/
checkPermissions(LocSettingActivity.this);
dialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(LocSettingActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
alertDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
And here I'm getting it in other class
LocSettingActivity locSettingActivity = new LocSettingActivity();
public void onResume() {
super.onResume();
locSettingActivity.checkPermissions(this);
}
public void openTuckLocation(View view) {
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
in=new Intent(PagerMainActivity.this, MapsActivityLocation.class);
}
else {
locSettingActivity.checkPermissions(this);
return;
}
}
insted of
locSettingActivity.checkPermissions(this);
use
locSettingActivity.checkPermissions(getApplicationContext);
Or
locSettingActivity.checkPermissions(ActivityName.this);
as sometimes context can also give error
Previously the below code works fine even on The Marshmellow but yesterday i got this eror first time Screen Overlay Detected while i am getting the Run time Location permission and during onPause() it W/Activity: Can reqeust only one set of permissions at a time and after that crash please help me i even follow so many answers but none is solved my problem.
Code:
OnCreate :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!isGooglePlayServicesAvailable()) {
Toast.makeText(getApplicationContext(),"Install Google Play Service",Toast.LENGTH_SHORT).show();
finish();
}
Boolean bool = checkPermission();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.e("overlayPro", String.valueOf(Settings.canDrawOverlays(this)));
}
setContentView(R.layout.activity_find_service_providers);
linearLayout =(LinearLayout)findViewById(R.id.linearlayout);
Intent intent = getIntent();
if(null!=intent.getExtras()) {
//get Intent value
}
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
onStart() :
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkPermission()) {
mapFrag.getMapAsync(this);
if (!Utility.isLocationEnabled(getApplicationContext())) {
setingsrequest();
}
} else {
checkLocationPermission();
}
}
else if (!Utility.isLocationEnabled(getApplicationContext())){
mapFrag.getMapAsync(this);
setingsrequest();
}
else {
mapFrag.getMapAsync(this);
}
}
checkPermission() :
private boolean checkPermission() {
return ( ContextCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
}
settingRequest():
public void setingsrequest()
{
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true); //this is the key ingredient
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
linearLayout.setVisibility(View.VISIBLE);
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(FindServiceProviders.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case 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.
break;
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PERM_REQUEST_CODE_DRAW_OVERLAYS) {
if (android.os.Build.VERSION.SDK_INT >= 23) { //Android M Or Over
if (!Settings.canDrawOverlays(this)) {
Log.e("overlayResult","false");
// ADD UI FOR USER TO KNOW THAT UI for SYSTEM_ALERT_WINDOW permission was not granted earlier...
}
}
}
switch (requestCode) {
// Check for the integer request code originally supplied to startResolutionForResult().
case REQUEST_CHECK_SETTINGS:
switch (resultCode) {
case Activity.RESULT_OK:
linearLayout.setVisibility(View.VISIBLE);
mapFrag.getMapAsync(this);
break;
case Activity.RESULT_CANCELED:
//setingsrequest();//keep asking if imp or do whatever
finish();
break;
}
break;
}
}
checkLocationPermission()
private void checkLocationPermission() {
if (!checkPermission()) {
// 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, R.style.MyDialogTheme)
.setTitle("Location Permission Needed")
.setMessage("This app needs the Location permission, please accept to use location functionality")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(FindServiceProviders.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
})
.create()
.show();
} else {
Log.e("overlay","detected but Request New Permission");
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_LOCATION);
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String permissions[],#NonNull 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 (checkPermission()) {
//allowed
Log.e("allowed", String.valueOf(permissions));
if (!Utility.isLocationEnabled(getApplicationContext())) {
setingsrequest();
}
if (mGoogleApiClient == null) {
buildGoogleApiClient();
}
mapFrag.getMapAsync(this);
}else{
//set to never ask again
Log.e("set to never ask again", String.valueOf(permissions));
//do something here.
}
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
checkLocationPermission();
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Updated Code :
#Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {
switch (permsRequestCode) {
case R_PERM: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
PermHandling();
}
else if (Build.VERSION.SDK_INT >= 23 && !shouldShowRequestPermissionRationale(permissions[0])) {
//Previously Permission Request was cancelled with 'Dont Ask Again',
// Redirect to Settings after showing Information about why you need the permission
AlertDialog.Builder builder = new AlertDialog.Builder(TestActivity.this,R.style.MyDialogTheme);
builder.setTitle("Need Storage Permission");
builder.setMessage("This app needs storage permission.");
builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
sentToSettings = true;
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
// Toast.makeText(getBaseContext(), "Go to Permissions to Grant Storage", Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
else {
new AlertDialog.Builder(this,R.style.MyDialogTheme)
.setTitle("Location Permission Needed")
.setMessage("This app needs the Location permission, please accept to use location functionality")
.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//Prompt the user once explanation has been shown
ActivityCompat.requestPermissions(TestActivity.this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
R_PERM );
}
})
.create()
.show();
}
return;
}
}
}
In onActivityResult :
if (requestCode == REQUEST_PERMISSION_SETTING) {
if (ActivityCompat.checkSelfPermission(TestActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
proceedAfterPermission();
}
}
How can I prompt the user to turn on Location?
The app is supposed to filter a list of locations with the current location of the user. If the user has the Location service turned off, the app should prompt the user asking for the Location to be turned on.
For instance Trip Advisor app does this:
( Not sure if I can post other apps screenshots here, but if I shouldn't be doing it, please say so. And apologize for the full sized images, tried to make them smaller, but SO didn't liked it... )
In the first image, you can see I have the Location service turned off. The, after opening the Trip Advisor app, and tapping the Near me now option, I'm prompted with the second image, where I'm asked to Turn on Location services. After I tap the button, a dialog shows up so I can allow, or disallow, the Location service to be turned on. If I tap OK, the Location service is turned on on the device, and the app consumes it.
How can I achieve this?
Found the solution I was asking for.
Requirements
Nuget Xamarin.GooglePlayServices.Location
Code
Int64
interval = 1000 * 60 * 1,
fastestInterval = 1000 * 50;
try {
GoogleApiClient
googleApiClient = new GoogleApiClient.Builder( this )
.AddApi( LocationServices.API )
.Build();
googleApiClient.Connect();
LocationRequest
locationRequest = LocationRequest.Create()
.SetPriority( LocationRequest.PriorityBalancedPowerAccuracy )
.SetInterval( interval )
.SetFastestInterval( fastestInterval );
LocationSettingsRequest.Builder
locationSettingsRequestBuilder = new LocationSettingsRequest.Builder()
.AddLocationRequest( locationRequest );
locationSettingsRequestBuilder.SetAlwaysShow( false );
LocationSettingsResult
locationSettingsResult = await LocationServices.SettingsApi.CheckLocationSettingsAsync(
googleApiClient, locationSettingsRequestBuilder.Build() );
if( locationSettingsResult.Status.StatusCode == LocationSettingsStatusCodes.ResolutionRequired ) {
locationSettingsResult.Status.StartResolutionForResult( this, 0 );
}
} catch( Exception exception ) {
// Log exception
}
With this code, if the locationSettingsResult.Status.StatusCode is LocationSettingsStatusCodes.ResolutionRequired ( 6 ) it means -- probably -- that the Location is turned off, although I've found one situation that it didn't return the value when the device had the option turned off. After turning on and off, it worked, might be a bug on the device, or not.
Suppose your are doing all this stuff in an Activity name LocationActivity. You need to implement some callbacks for this purpose. Below is the code with comments so you can easily understand which method do what and when is called.
Keep in mind to add permissions in app manifest file:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Code for Ativity:
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.v13.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
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.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
public class LocationActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
// Unique tag for the error dialog fragment
private static final String DIALOG_ERROR = "dialog_error";
// Bool to track whether the app is already resolving an error
private boolean mResolvingError = false;
// Request code to use when launching the resolution activity
private static final int REQUEST_RESOLVE_ERROR = 555;
int ACCESS_FINE_LOCATION_CODE = 3310;
int ACCESS_COARSE_LOCATION_CODE = 3410;
private GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Build Google API Client for Location related work
buildGoogleApiClient();
}
// When user first come to this activity we try to connect Google services for location and map related work
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
// Google Api Client is connected
#Override
public void onConnected(Bundle bundle) {
if (mGoogleApiClient.isConnected()) {
//if connected successfully show user the settings dialog to enable location from settings services
// If location services are enabled then get Location directly
// Else show options for enable or disable location services
settingsrequest();
}
}
// This is the method that will be called if user has disabled the location services in the device settings
// This will show a dialog asking user to enable location services or not
// If user tap on "Yes" it will directly enable the services without taking user to the device settings
// If user tap "No" it will just Finish the current Activity
public void settingsrequest() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
builder.setAlwaysShow(true); //this is the key ingredient
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
if (mGoogleApiClient.isConnected()) {
// check if the device has OS Marshmellow or greater than
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (ActivityCompat.checkSelfPermission(LocationActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(LocationActivity.this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(LocationActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, ACCESS_FINE_LOCATION_CODE);
} else {
// get Location
}
} else {
// get Location
}
}
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(LocationActivity.this, REQUEST_RESOLVE_ERROR);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case 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.
break;
}
}
});
}
// This method is called only on devices having installed Android version >= M (Marshmellow)
// This method is just to show the user options for allow or deny location services at runtime
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 3310: {
if (grantResults.length > 0) {
for (int i = 0, len = permissions.length; i < len; i++) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
// Show the user a dialog why you need location
} else if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
// get Location
} else {
this.finish();
}
}
}
return;
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_RESOLVE_ERROR) {
mResolvingError = false;
switch (resultCode) {
case Activity.RESULT_OK:
// get location method
break;
case Activity.RESULT_CANCELED:
this.finish();
break;
}
}
}
#Override
public void onConnectionSuspended(int i) {
}
// When there is an error connecting Google Services
#Override
public void onConnectionFailed(ConnectionResult result) {
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (result.hasResolution()) {
try {
mResolvingError = true;
result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
} catch (IntentSender.SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
// Show dialog using GoogleApiAvailability.getErrorDialog()
showErrorDialog(result.getErrorCode());
mResolvingError = true;
}
}
/* Creates a dialog for an error message */
private void showErrorDialog(int errorCode) {
// Create a fragment for the error dialog
ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
// Pass the error that should be displayed
Bundle args = new Bundle();
args.putInt(DIALOG_ERROR, errorCode);
dialogFragment.setArguments(args);
dialogFragment.show(getSupportFragmentManager(), "errordialog");
}
/* Called from ErrorDialogFragment when the dialog is dismissed. */
public void onDialogDismissed() {
mResolvingError = false;
}
/* A fragment to display an error dialog */
public static class ErrorDialogFragment extends DialogFragment {
public ErrorDialogFragment() {
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Get the error code and retrieve the appropriate dialog
int errorCode = this.getArguments().getInt(DIALOG_ERROR);
return GoogleApiAvailability.getInstance().getErrorDialog(
this.getActivity(), errorCode, REQUEST_RESOLVE_ERROR);
}
#Override
public void onDismiss(DialogInterface dialog) {
((LocationActivity) getActivity()).onDialogDismissed();
}
}
// Connect Google Api Client if it is not connected already
#Override
protected void onStart() {
super.onStart();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
}
// Stop the service when we are leaving this activity
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
}
}
You can read the official docs here
You can use the following class for moving user to settings. First check for the location if it is not available call showSettingAlert
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
// Setting Dialog Title
alertDialog.setTitle("GPS is settings");
// Setting Dialog Message
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
Use the below code snippet to open Device settings screen.
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
else use settings API for permission dialog
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(mContext).addApi(LocationServices.API).build();
googleApiClient.connect();
LocationRequest lReq = LocationRequest.create();
lReq.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
lReq.setInterval(10000);
lReq.setFastestInterval(10000 / 2);
LocationSettingsRequest.Builder lBuilder = new LocationSettingsRequest.Builder().addLocationRequest(lReq);
lBuilder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, lBuilder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
break;
}
}
});
Try
private void turnGPSOn(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(!provider.contains("gps")){ //if gps is disabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
private void turnGPSOff(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(provider.contains("gps")){ //if gps is enabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
private boolean canToggleGPS() {
PackageManager pacman = getPackageManager();
PackageInfo pacInfo = null;
try {
pacInfo = pacman.getPackageInfo("com.android.settings", PackageManager.GET_RECEIVERS);
} catch (NameNotFoundException e) {
return false; //package not found
}
if(pacInfo != null){
for(ActivityInfo actInfo : pacInfo.receivers){
//test if recevier is exported. if so, we can toggle GPS.
if(actInfo.name.equals("com.android.settings.widget.SettingsAppWidgetProvider") && actInfo.exported){
return true;
}
}
}
return false; //default
}
How to turn on location programmatically in android by showing a location dialog in your app to the user.
This is simple code :
First, add dependencies of google service location
dependencies {
implementation 'com.google.android.gms:play-services-location:17.0.0'
}
in MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button TurnGPSOn;
private LocationRequest locationRequest;
private static final int REQUEST_CHECK_SETTINGS = 10001;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TurnGPSOn = findViewById(R.id.location_btn);
TurnGPSOn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(5000);
locationRequest.setFastestInterval(2000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
Task<LocationSettingsResponse> result = LocationServices.getSettingsClient(getApplicationContext())
.checkLocationSettings(builder.build());
result.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
#Override
public void onComplete(#NonNull Task<LocationSettingsResponse> task) {
try {
LocationSettingsResponse response = task.getResult(ApiException.class);
Toast.makeText(MainActivity.this, "GPS is already tured on", Toast.LENGTH_SHORT).show();
} catch (ApiException e) {
switch (e.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException resolvableApiException = (ResolvableApiException)e;
resolvableApiException.startResolutionForResult(MainActivity.this,REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException ex) {
ex.printStackTrace();
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
//Device does not have location
break;
}
}
}
});
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CHECK_SETTINGS) {
switch (resultCode) {
case Activity.RESULT_OK:
Toast.makeText(this, "GPS is tured on", Toast.LENGTH_SHORT).show();
case Activity.RESULT_CANCELED:
Toast.makeText(this, "GPS required to be tured on", Toast.LENGTH_SHORT).show();
}
}
}
}
in activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="#+id/location_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Turn On GPS"
android:textAllCaps="false"
android:textSize="16sp" />
</LinearLayout>
the source: https://github.com/Pritish-git/turnOn_location
Part of my app requires location services, so if location is currently turned off, the app will prompt the user to enable it. Here is how I am doing it: (Also seen in this Stack Overflow answer)
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>()
{
#Override
public void onResult(LocationSettingsResult result)
{
final Status status = result.getStatus();
final LocationSettingsStates = result.getLocationSettingsStates();
switch (status.getStatusCode())
{
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
...
Log.d("onResult", "SUCCESS");
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
Log.d("onResult", "RESOLUTION_REQUIRED");
try
{
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(OuterClass.this, REQUEST_LOCATION);
}
catch (SendIntentException e)
{
// Ignore the error.
}
break;
case 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.
...
Log.d("onResult", "UNAVAILABLE");
break;
}
}
});
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
// This log is never called
Log.d("onActivityResult()", Integer.toString(resultCode));
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
switch (requestCode)
{
case REQUEST_LOCATION:
switch (resultCode)
{
case Activity.RESULT_OK:
{
// All required changes were successfully made
break;
}
case Activity.RESULT_CANCELED:
{
// The user was asked to change settings, but chose not to
break;
}
default:
{
break;
}
}
break;
}
}
This code works well, however, onActivityResult() is always skipped. Whether or not the user presses Yes, No, or back from the Dialog, onActivityResult() doesn't run.
I need Android to call onActivityResult() so if the user chooses not to turn on location services, I can handle it appropriately.
Google's developer page (and the code above) explicitly says that onActivityResult() should be called. Anyone know why it's being skipped?
I also don't know what the purpose of this line is:
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
Thanks!
Edit: Basic information on the structure of my app:
This code is contained within the onResume() method of a Fragment which implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, and LocationListener to receive location updates. Example seen here.
In onLocationChanged() the Fragment will have a custom View call invalidate() and re-draw itself with updated information.
UPDATE
The original answer below is using Java and the now deprecated SettingsApi.
Here is a more modern approach using Kotlin and SettingsClient:
fun showEnableLocationSetting() {
activity?.let {
val locationRequest = LocationRequest.create()
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
val task = LocationServices.getSettingsClient(it)
.checkLocationSettings(builder.build())
task.addOnSuccessListener { response ->
val states = response.locationSettingsStates
if (states.isLocationPresent) {
//Do something
}
}
task.addOnFailureListener { e ->
if (e is ResolvableApiException) {
try {
// Handle result in onActivityResult()
e.startResolutionForResult(it,
MainActivity.LOCATION_SETTING_REQUEST)
} catch (sendEx: IntentSender.SendIntentException) { }
}
}
}
}
In MainActivity, define the constant:
companion object {
const val LOCATION_SETTING_REQUEST = 999
}
ORIGINAL ANSWER:
It looks like the main issue is that you have all of the code in a Fragment, and since startResolutionForResult() needs an Activity passed into it, the Activity is what gets the onActivityResult() callback.
One way to get around that is to use the technique described here, manually call the Fragment's onActivityResult() method from the Activity when the result comes in.
I just got this simple example working.
First, the Activity, which adds the Fragment, and also has functionality to pass along the result of onActivityResult() to the Fragment:
public class MainActivity extends AppCompatActivity{
LocationFragment lFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lFrag = LocationFragment.newInstance();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, lFrag).commit();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == LocationFragment.REQUEST_LOCATION){
lFrag.onActivityResult(requestCode, resultCode, data);
}
else {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
Here is the Fragment, which contains all of the functionality to show the dialog, and handle the result. In this simple example I just used Toast messages to verify that it is working as expected. Note that the main change that I've made here from the code in your question is the use of getActivity() to get the Activity reference needed for the call to startResolutionForResult().
public class LocationFragment extends Fragment
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
PendingResult<LocationSettingsResult> result;
final static int REQUEST_LOCATION = 199;
public static LocationFragment newInstance() {
LocationFragment fragment = new LocationFragment();
return fragment;
}
public LocationFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
mGoogleApiClient.connect();
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_location, container, false);
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(30 * 1000);
mLocationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
//final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
//...
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(
getActivity(),
REQUEST_LOCATION);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case 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.
//...
break;
}
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d("onActivityResult()", Integer.toString(resultCode));
//final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
switch (requestCode)
{
case REQUEST_LOCATION:
switch (resultCode)
{
case Activity.RESULT_OK:
{
// All required changes were successfully made
Toast.makeText(getActivity(), "Location enabled by user!", Toast.LENGTH_LONG).show();
break;
}
case Activity.RESULT_CANCELED:
{
// The user was asked to change settings, but chose not to
Toast.makeText(getActivity(), "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
break;
}
default:
{
break;
}
}
break;
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
Here are the results visually, first the dialog is shown if Location Mode is disabled:
Then, if the user clicks No, the result is passed from the Activity to the Fragment, which shows a Toast:
Same thing when the user clicks Yes, but with a success result, and Location Mode is enabled:
Note that it might be a better option to just keep all of this functionality in the Activity, and then call into a public method in the Fragment when the result comes in.
Here is fully working code for keeping the functionality in the Activity.
Of course in this solution, you would need to add a call into the Fragment to update the state of Location Mode after onActivityResult() is called.
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
PendingResult<LocationSettingsResult> result;
final static int REQUEST_LOCATION = 199;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(30 * 1000);
mLocationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
//final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
//...
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(
MainActivity.this,
REQUEST_LOCATION);
} catch (SendIntentException e) {
// Ignore the error.
}
break;
case 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.
//...
break;
}
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d("onActivityResult()", Integer.toString(resultCode));
//final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
switch (requestCode)
{
case REQUEST_LOCATION:
switch (resultCode)
{
case Activity.RESULT_OK:
{
// All required changes were successfully made
Toast.makeText(MainActivity.this, "Location enabled by user!", Toast.LENGTH_LONG).show();
break;
}
case Activity.RESULT_CANCELED:
{
// The user was asked to change settings, but chose not to
Toast.makeText(MainActivity.this, "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
break;
}
default:
{
break;
}
}
break;
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
You need to add this to your result callback:
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
fragment.startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CHECK_SETTINGS, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
onActivityResult will be called on your fragment, you don't need to call it manually in your activity. This is essentially how startResolutionForResult works.
When you need to resolve the Status or the ResolvableApiException, I suggest you to leverage the activity.registerForActivityResult API in place of startResolutionForResult:
ActivityResultLauncher<IntentSenderRequest> launcher = activity.registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// All required changes were successfully made
} else {
// The user was asked to change settings, but chose not to
}
}
});
IntentSenderRequest intentSenderRequest = new IntentSenderRequest.Builder(exception.getResolution()).build();
launcher.launch(intentSenderRequest);
You are using Java, but in case Kotlin is needed:
val launcher = activity.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// User accepted
} else {
// User didn't accepted
}
}
val intentSenderRequest = IntentSenderRequest.Builder(exception.resolution).build()
launcher.launch(intentSenderRequest)
If you want results back to your fragment than use
startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
instead of status.startResolutionForResult(YourActivity, LOCATION_REQUEST);
USING above method will deliver result back to your fragment only.
For Kotlin Users
This solution is applicable for both Activity and Fragment by doing one following change in checkLocationSetting():
For Activity resolvableApiException.startResolutionForResult(this#MainActivity, REQUEST_CHECK_SETTING)
For Fragment
startIntentSenderForResult(resolvableApiException.resolution.intentSender, REQUEST_CHECK_SETTING, null, 0, 0,0,null)
By using LocationSettingsResponse this task can be achieved.
inside MainActivity.kt
private fun checkLocationSetting()
{
locationRequest = LocationRequest.create()
locationRequest.apply {
priority=LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 5000
fastestInterval = 2000
}
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
val result: Task<LocationSettingsResponse> = LocationServices.getSettingsClient(applicationContext)
.checkLocationSettings(builder.build())
result.addOnCompleteListener {
try{
val response: LocationSettingsResponse = it.getResult(ApiException::class.java)
Toast.makeText(this#MainActivity, "GPS is On", Toast.LENGTH_SHORT).show()
Log.d(TAG, "checkSetting: GPS On")
}catch(e:ApiException){
when(e.statusCode){
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->{
val resolvableApiException = e as ResolvableApiException
resolvableApiException.startResolutionForResult(this#MainActivity, REQUEST_CHECK_SETTING)
Log.d(TAG, "checkSetting: RESOLUTION_REQUIRED")
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
// USER DEVICE DOES NOT HAVE LOCATION OPTION
}
}
}
}
}
onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode)
{
REQUEST_CHECK_SETTING ->{
when(resultCode){
Activity.RESULT_OK->{
Toast.makeText(this#MainActivity, "GPS is Turned on", Toast.LENGTH_SHORT).show()
}
Activity.RESULT_CANCELED ->{
Toast.makeText(this#MainActivity, "GPS is Required to use this app", Toast.LENGTH_SHORT).show()
}
}
}
}
}
Link to complete code MainActivity.kt
Output:
Link to complete code MainActivity.kt
For handling enable location from fragment below is the latest code that can be used. Settings API is now deprecated. Below is the method to use SettingsClient API.
Also I noticed that, in Android 10 devices even when user enable the location; status result in onActivityResult is coming as RESULT_CANCELED, I couldn't find a way to get rid of that issue in Android 10 device where as in Android PIE the result code is RESULT_OK. So only way to detect whether user enabled it or not is by explicitly checking whether location is enabled using LocationManagerCompat API for Android 10 devices
private fun enableLocationIfRequired() {
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(LocationRequest().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY))
.setAlwaysShow(true)
val settingsClient = LocationServices.getSettingsClient(context!!)
val task = settingsClient!!.checkLocationSettings(builder.build())
task.addOnCompleteListener {
try {
val response = it.getResult(ApiException::class.java)
//Success
Log.d(javaClass.simpleName, "Location is enabled")
} catch (exception: ApiException) {
Log.d(javaClass.simpleName, "exception thrown: ${exception.statusCode}")
when (exception.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
// Location settings are not satisfied. But could be fixed by showing the
// user a dialog.
try {
// Cast to a resolvable exception.
val resolvable = exception as ResolvableApiException
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
Log.d(javaClass.simpleName, "startResolutionForResult called")
this.startIntentSenderForResult(
resolvable.resolution.intentSender,
RC_LOCATION_ENABLE,
null, 0, 0, 0, null
)
} catch (e: IntentSender.SendIntentException) {
// Ignore the error.
Log.d(javaClass.simpleName, "IntentSender.SendIntentException")
} catch (e: ClassCastException) {
// Ignore, should be an impossible error.
Log.d(javaClass.simpleName, "ClassCastException")
}
}
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.
}
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RC_LOCATION_ENABLE -> {
if (resultCode == Activity.RESULT_OK) {
Log.d(javaClass.simpleName, "Location is enabled by user")
} else {
Log.d(javaClass.simpleName, "Location enable request is cancelled by user")
}
val lm = context!!.getSystemService(LOCATION_SERVICE) as LocationManager
if (LocationManagerCompat.isLocationEnabled(lm)) {
Log.d(javaClass.simpleName, "Location is enabled by user")
} else {
Log.d(javaClass.simpleName, "Location enable request is cancelled by user")
}
}
}
}
Thanks to #gianlucaparadise solution you should write for new API:
Fragment (or maybe Activity):
private lateinit var checkLocationSettings: ActivityResultLauncher<IntentSenderRequest>
override fun onCreate(savedInstanceState: Bundle?) {
checkLocationSettings =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
if (result.resultCode == RESULT_OK) {
// GPS is turned on in system settings.
}
}
}
Fragment or utility class where you want to enable GPS (see 1 or 2):
.addOnFailureListener(context) { e ->
when ((e as? ApiException)?.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
try {
// Cast to a resolvable exception.
val resolvable = e as ResolvableApiException
// Old API: show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
// New API: call registerForActivityResult::launch
// and check the result in callback.
val intentSenderRequest =
IntentSenderRequest.Builder(resolvable.resolution).build()
checkLocationSettings.launch(intentSenderRequest)
} catch (sie: IntentSender.SendIntentException) {
Timber.e("GPS: Unable to execute request.")
} catch (cce: java.lang.ClassCastException) {
// Ignore, should be an impossible error.
Timber.e("GPS: Unable to execute request, ClassCastException.")
}
Deprecated API variant for Fragment and onActivityResult: LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped.
I see that you use different constants REQUEST_CHECK_SETTINGS and REQUEST_LOCATION for request code. Do they have same value?
For the code:final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);.
The purpose of above code is to get the current status of Location setting(like use Network, GPS, ...) after changed the setting.
Also, in your code, I think it's should be LocationSettingsStates.fromIntent(data); because the intent doesn't exixst here, maybe it's just a typo.
Its because of all google api codes present in the Fragments.. Try the following it will help to overcome...
1.Create a empty constructor for your fragments.
2.need oncreate() method before the onCreateView()...
3.paste the Google api code inside the oncreate()....
public mainFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
buildGoogleApiClient();
buildLocationSettingsRequest();
checkLocationSettings();
mGoogleApiClient.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
For your Reference...
Click here...
Saving fragment field in activity (as Daniel suggested) is not often a good decision, cause imagine you have multiple fragments and each contains location code. I did it in a different manner:
public class MainActivity extends Activity implements PlaceFragment.SettingsModifyHandler {
private static final int LOCATION_SETTINGS_RESULT = 1;
private OnResultCallback placeCallback;
...
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == LOCATION_SETTINGS_RESULT) {
if (resultCode == Activity.RESULT_OK) {
placeCallback.resultOk();
} else {
placeCallback.resultFail();
}
placeCallback = null;
}
}
#Override
public void handle(IntentSender intentSender, OnResultCallback callback) {
placeCallback = callback;
try {
startIntentSenderForResult(intentSender, LOCATION_SETTINGS_RESULT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
callback.resultFail();
}
}
}
public class PlaceFragment extends Fragment {
private SettingsModifyHandler settingsModifyHandler;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (context instanceof SettingsModifyHandler) {
settingsModifyHandler = (SettingsModifyHandler) context;
} else {
throw new RuntimeException("Parent activity must implement PlaceFragment.SettingsModifyHandler interface");
}
}
/* Callback from module, where you implemented status.getStatusCode().LocationSettingsStatusCodes.RESOLUTION_REQUIRED case
(status is instance of com.google.android.gms.common.api.Status)
You provide intentSender here through status.getResolution().getIntentSender() */
#Override
public void placeLoadError(IntentSender sender) {
TextView view_text = (TextView) root.findViewById(R.id.text_error);
TextView view_btn = (TextView) root.findViewById(R.id.btn_reply);
view_text.setText("Need to change location settings");
view_btn.setText("Change");
view_btn.setOnClickListener(v -> {
settingsModifyHandler.handle(sender, new SettingsModifyHandler.OnResultCallback() {
#Override
public void resultOk() {
presenter.loadPlace(placeId);
}
#Override
public void resultFail() {
ToastUtils.show("You should change location settings!");
}
});
});
}
public interface SettingsModifyHandler {
void handle(IntentSender intentSender, OnResultCallback callback);
interface OnResultCallback {
void resultOk();
void resultFail();
}
}
}
I am developing an android app which needs to activate the GPS.
I read a lot of topics in a lot of forums and the answer I've found is:
it's not possible
But... the "Cerberus" APP turns my GPS on... so... it's possible!
Can anyone help me with this?
No, it's impossible, and inappropriate. You can't just manage the user's phone without their authority. The user must interact to enable GPS.
From Play Store:
"Cerberus automatically enables GPS if it is off when you try to localize your device (only on Android < 2.3.3) and you can protect it from unauthorized uninstalling - more info in the app configuration."
You can do something like this:
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
I think we have more better version to enable the location without opening the settings just like google map works.
It will looks like this -
Add Dependency in gradle -
compile 'com.google.android.gms:play-services-location:10.0.1'
public class MapActivity extends AppCompatActivity {
protected static final String TAG = "LocationOnOff";
private GoogleApiClient googleApiClient;
final static int REQUEST_LOCATION = 199;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setFinishOnTouchOutside(true);
// Todo Location Already on ... start
final LocationManager manager = (LocationManager) MapActivity.this.getSystemService(Context.LOCATION_SERVICE);
if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(MapActivity.this)) {
Toast.makeText(MapActivity.this,"Gps already enabled",Toast.LENGTH_SHORT).show();
}
// Todo Location Already on ... end
if(!hasGPSDevice(MapActivity.this)){
Toast.makeText(MapActivity.this,"Gps not Supported",Toast.LENGTH_SHORT).show();
}
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(MapActivity.this)) {
Log.e("TAG","Gps already enabled");
Toast.makeText(MapActivity.this,"Gps not enabled",Toast.LENGTH_SHORT).show();
enableLoc();
}else{
Log.e("TAG","Gps already enabled");
Toast.makeText(MapActivity.this,"Gps already enabled",Toast.LENGTH_SHORT).show();
}
}
private boolean hasGPSDevice(Context context) {
final LocationManager mgr = (LocationManager) context
.getSystemService(Context.LOCATION_SERVICE);
if (mgr == null)
return false;
final List<String> providers = mgr.getAllProviders();
if (providers == null)
return false;
return providers.contains(LocationManager.GPS_PROVIDER);
}
private void enableLoc() {
if (googleApiClient == null) {
googleApiClient = new GoogleApiClient.Builder(MapActivity.this)
.addApi(LocationServices.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
googleApiClient.connect();
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("Location error","Location error " + connectionResult.getErrorCode());
}
}).build();
googleApiClient.connect();
}
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(MapActivity.this, REQUEST_LOCATION);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
}
}
});
}
}
There used to be an exploit that allowed the GPS to be turned on by an app with no special permissions. That exploit no longer exists as of 2.3 (in most ROMs). Here's another post that talks about it,
How can I enable or disable the GPS programmatically on Android?
"GPS enabled" is a secure setting, so you must have WRITE_SECURE_SETTINGS permission. This is a signature protected permission however, so you app will not be granted this unless it is signed with the manufacturer's platform certificate.
The correct thing is to send the user to the location settings page, and let them enable GPS if they wish. e.g.,
Intent i = new
Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(i);
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.gps_disabled_message)
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
This creates an alert and allows the user to go to the settings screen and hit the back button to come right back to your app. The power widget exploit doesn't work beyond 2.3 to my knowledge.
Use this code
//turnGPSON called After setcontentView(xml)
private void turnGPSOn() {
String provider = android.provider.Settings.Secure.getString(
getContentResolver(),
android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if (!provider.contains("gps")) { // if gps is disabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings",
"com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}**
The GoogleApiClient has been deprecated, we need to use SettingsClient and GoogleApi to enable GPS without going to the Location Settings just like Google Maps, OLA, Zomato etc. Below code supports any of the Android Version starting from 4.4 or lower to 11+
dependency required in gradle file:
implementation 'com.google.android.gms:play-services-location:17.1.0'
To check GPS state ON or OFF:
private static LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
public static boolean isGpsEnabled(){
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
If GPS is already enabled then we will display toast notification otherwise will ask to turn the GPS on.
//Defining constant request code, add in your activity class
private static final int REQUEST_CHECK_SETTINGS = 111;
if(!isGpsEnabled()){
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true); //this displays dialog box like Google Maps with two buttons - OK and NO,THANKS
Task<LocationSettingsResponse> task =
LocationServices.getSettingsClient(this).checkLocationSettings(builder.build());
task.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
#Override
public void onComplete(Task<LocationSettingsResponse> task) {
try {
LocationSettingsResponse response = task.getResult(ApiException.class);
// All location settings are satisfied. The client can initialize location
// requests here.
} catch (ApiException exception) {
switch (exception.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the
// user a dialog.
try {
// Cast to a resolvable exception.
ResolvableApiException resolvable = (ResolvableApiException) exception;
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
resolvable.startResolutionForResult(
YOUR_ACTIVITY.this,
REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
} catch (ClassCastException e) {
// Ignore, should be an impossible error.
}
break;
case 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.
break;
}
}
}
});
} else {
Toast.makeText(getApplicationContext(), "GPS is already Enabled!", Toast.LENGTH_SHORT).show();
}
}
If GPS is not ON, then flow will come to RESOLUTION_REQUIRED and that will call startResolutionForResult which will be handled by onActivityResult
Now, add onActivityResult method -
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
switch (requestCode) {
case REQUEST_CHECK_SETTINGS:
switch (resultCode) {
case Activity.RESULT_OK:
// All required changes were successfully made
Toast.makeText(getApplicationContext(),"User has clicked on OK - So GPS is on", Toast.LENGTH_SHORT).show();
break;
case Activity.RESULT_CANCELED:
// The user was asked to change settings, but chose not to
Toast.makeText(getApplicationContext(),"User has clicked on NO, THANKS - So GPS is still off.", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
break;
}
}
If resultCode is RESULT_OK that means user allowed to turn GPS on else for RESULT_CANCELED, you can again ask or show the rationale dialog.
You can wrap above code in method and call wherever you require
While building LocationRequest you can set Interval, Priority, SmallestDisplacement, FastestInterval, etc as per you required by your app.
We have used Task api instead of PendingResult
For Official Document, refer - https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient
you might want to check out this thread
How can I enable or disable the GPS programmatically on Android?
here are the codes copied from that thread
private void turnGPSOn(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(!provider.contains("gps")){ //if gps is disabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
private void turnGPSOff(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(provider.contains("gps")){ //if gps is enabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
but the solution is not recommended as it will not be available to android version > 2.3 supposingly.. do check the comments