LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped - android

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();
}
}
}

Related

How to get location from background service when location is turned off? [duplicate]

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();
}
}
}

Access location permission from another Activity

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

Start resolution for result in a fragment

In my app if the user doesn't have the location turned on I am prompting with a dialog and then trying to return that result (probably incorrectly) by overriding on activity result.
This is inside a fragment so not sure how that changes things:
This is how I am calling it the dialog with startResolutionForResult:
public void checkDeviceLocationIsOn()
{
System.out.println("Test running setting request" );
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER);
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.
System.out.println("Test setting all fine starting location request" );
getLocation();
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(), LOCATION_SETTINGS_REQUEST_CODE);
System.out.println("Test setting not met starting dialog to prompt user" );
} 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;
}
}
});
}
And then I try to get the result like this below it (This never gets called):
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
// Check for the integer request code originally supplied to startResolutionForResult().
case LOCATION_SETTINGS_REQUEST_CODE:
switch (resultCode) {
case Activity.RESULT_OK:
System.out.println("test user has turned the gps back on");
getLocation();
break;
case Activity.RESULT_CANCELED:
System.out.println("test user has denied the gps to be turned on");
Toast.makeText(getActivity(), "Location is required to order stations", Toast.LENGTH_SHORT).show();
break;
}
break;
}
}
Thanks in advance for your help
Call the fragment method
startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
instead of
status.startResolutionForResult(...)
To start resolution for result in your fragment you should use the new API
first, create the ActivityResultLanucher
private val resolutionForResult =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
if (activityResult.resultCode == RESULT_OK)
//startLocationUpdates() or do whatever you want
else {
showMessage("we can't determine your location")
}
}
then use it like this
val intentSenderRequest = IntentSenderRequest.Builder(exception.resolution).build()
resolutionForResult.launch(intentSenderRequest)
in Super Activity Perform this Method...
in MainActivity
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
final int REQUEST_CHECK_SETTINGS = 100;//check GPS setting
Log.d("onActivityResult()", Integer.toString(resultCode));
//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(this, "Location enabled by user!", Toast.LENGTH_LONG).show();
ContactFragment.isGPSEnabled=true;
ContactFragment.click();
break;
}
case Activity.RESULT_CANCELED:
{
// The user was asked to change settings, but chose not to
Toast.makeText(this, "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
ContactFragment.isGPSEnabled=false;
break;
}
default:
{
break;
}
}
break;
}
}
inside ContactFragment
#Override
public void onConnected(#Nullable 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(googleApiClient, builder.build());
result.setResultCallback(this);
}
#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_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;
}
}
Maybe an easy solution is to use two activities and show one like a dialog by using any dialog theme on the Manifest
<activity
android:name=".LocationDialogActivity"
android:theme="#style/DialogStyle"></activity>
them just use the onActivityResult as you used it always.
DialogActivity
private void setLocation() {
Intent intentResult = new Intent();
intentResult.putExtra("latitude", "valueLat");
intentResult.putExtra("longitude", "valueLon");
setResult(RESULT_OK, intentResult);
finish();
}
MainActivity
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
/*Take your data*/
latitude = data.getExtras().getString("latitude");
longitude = data.getExtras().getString("longitude");
}
}

How to prompt user to turn on location settings like Google Maps? [duplicate]

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();
}
}
}

Turn on location services without navigating to settings page

Against the traditional approach of prompting user to go to the settings page and enable location services and come back again, I have noticed a simpler way of doing the same in some of the latest apps.
Referring to below screenshot, it prompts a dialog to user to enable the location services with just one click and it works in those apps.
How can I achieve the same?
This dialog is created by LocationSettingsRequest.Builder available in the Google Play Services.
You need to add a dependency to your app build.gradle:
compile 'com.google.android.gms:play-services-location:10.0.1'
Then you can use this minimal example:
private void displayLocationSettingsRequest(Context context) {
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).build();
googleApiClient.connect();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(10000 / 2);
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.SUCCESS:
Log.i(TAG, "All location settings are satisfied.");
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
Log.i(TAG, "Location settings are not satisfied. Show the user a dialog to upgrade location settings ");
try {
// Show the dialog by calling startResolutionForResult(), and check the result
// in onActivityResult().
status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
Log.i(TAG, "PendingIntent unable to execute request.");
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
Log.i(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog not created.");
break;
}
}
});
}
You can find the complete example here.
Follow the steps mentioned below
1) Create a LocationRequest as per your wish
LocationRequest mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(10 * 1000)
.setFastestInterval(1 * 1000);
2) Create a LocationSettingsRequest.Builder
LocationSettingsRequest.Builder settingsBuilder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
settingsBuilder.setAlwaysShow(true);
3) Get LocationSettingsResponse Task using following code
Task<LocationSettingsResponse> result = LocationServices.getSettingsClient(this)
.checkLocationSettings(settingsBuilder.build());
Note: LocationServices.SettingsApi is deprecated so, use SettingsClient Instead.
4) Add a OnCompleteListener to get the result from the Task.When the Task completes, the client can check the location settings by looking at the status code from the LocationSettingsResponse object.
result.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
#Override
public void onComplete(#NonNull Task<LocationSettingsResponse> task) {
try {
LocationSettingsResponse response =
task.getResult(ApiException.class);
} catch (ApiException ex) {
switch (ex.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException resolvableApiException =
(ResolvableApiException) ex;
resolvableApiException
.startResolutionForResult(MapsActivity.this,
LOCATION_SETTINGS_REQUEST);
} catch (IntentSender.SendIntentException e) {
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
break;
}
}
}
});
CASE 1: LocationSettingsStatusCodes.RESOLUTION_REQUIRED : Location is not enabled but, we can ask the user to enable the location by prompting him to turn on the location with the dialog (by calling startResolutionForResult).
CASE 2: 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.
5) OnActivityResult we can get the user action in the location settings dialog. RESULT_OK => User turned on the Location. RESULT_CANCELLED - User declined the location setting request.
Its Working Similar to google maps
Add Dependency in build.gradle file
compile 'com.google.android.gms:play-services:8.3.0'
this or that
compile 'com.google.android.gms:play-services-location:10.0.1'
import android.content.Context;
import android.content.IntentSender;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
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;
import java.util.List;
public class LocationOnOff_Similar_To_Google_Maps 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) LocationOnOff_Similar_To_Google_Maps.this.getSystemService(Context.LOCATION_SERVICE);
if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(LocationOnOff_Similar_To_Google_Maps.this)) {
Toast.makeText(LocationOnOff_Similar_To_Google_Maps.this,"Gps already enabled",Toast.LENGTH_SHORT).show();
finish();
}
// Todo Location Already on ... end
if(!hasGPSDevice(LocationOnOff_Similar_To_Google_Maps.this)){
Toast.makeText(LocationOnOff_Similar_To_Google_Maps.this,"Gps not Supported",Toast.LENGTH_SHORT).show();
}
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && hasGPSDevice(LocationOnOff_Similar_To_Google_Maps.this)) {
Log.e("keshav","Gps already enabled");
Toast.makeText(LocationOnOff_Similar_To_Google_Maps.this,"Gps not enabled",Toast.LENGTH_SHORT).show();
enableLoc();
}else{
Log.e("keshav","Gps already enabled");
Toast.makeText(LocationOnOff_Similar_To_Google_Maps.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(LocationOnOff_Similar_To_Google_Maps.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(LocationOnOff_Similar_To_Google_Maps.this, REQUEST_LOCATION);
finish();
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
}
}
});
}
}
}
implementation 'com.google.android.gms:play-services-location:16.0.0'
Variable Declaration
private SettingsClient mSettingsClient;
private LocationSettingsRequest mLocationSettingsRequest;
private static final int REQUEST_CHECK_SETTINGS = 214;
private static final int REQUEST_ENABLE_GPS = 516;
Open Dialog Using Below Code
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(new LocationRequest().setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY));
builder.setAlwaysShow(true);
mLocationSettingsRequest = builder.build();
mSettingsClient = LocationServices.getSettingsClient(YourActivity.this);
mSettingsClient
.checkLocationSettings(mLocationSettingsRequest)
.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
#Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
//Success Perform Task Here
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(YourActivity.this, REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException sie) {
Log.e("GPS","Unable to execute request.");
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
Log.e("GPS","Location settings are inadequate, and cannot be fixed here. Fix in Settings.");
}
}
})
.addOnCanceledListener(new OnCanceledListener() {
#Override
public void onCanceled() {
Log.e("GPS","checkLocationSettings -> onCanceled");
}
});
onActivityResult
#Override
public 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:
//Success Perform Task Here
break;
case Activity.RESULT_CANCELED:
Log.e("GPS","User denied to access location");
openGpsEnableSetting();
break;
}
} else if (requestCode == REQUEST_ENABLE_GPS) {
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
boolean isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!isGpsEnabled) {
openGpsEnableSetting();
} else {
navigateToUser();
}
}
}
private void openGpsEnableSetting() {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, REQUEST_ENABLE_GPS);
}
Thanks to Mattia Maestrini +1
Xamarin solution:
using Android.Gms.Common.Apis;
using Android.Gms.Location;
public const int REQUEST_CHECK_SETTINGS = 0x1;
private void DisplayLocationSettingsRequest()
{
var googleApiClient = new GoogleApiClient.Builder(this).AddApi(LocationServices.API).Build();
googleApiClient.Connect();
var locationRequest = LocationRequest.Create();
locationRequest.SetPriority(LocationRequest.PriorityHighAccuracy);
locationRequest.SetInterval(10000);
locationRequest.SetFastestInterval(10000 / 2);
var builder = new LocationSettingsRequest.Builder().AddLocationRequest(locationRequest);
builder.SetAlwaysShow(true);
var result = LocationServices.SettingsApi.CheckLocationSettings(googleApiClient, builder.Build());
result.SetResultCallback((LocationSettingsResult callback) =>
{
switch (callback.Status.StatusCode)
{
case LocationSettingsStatusCodes.Success:
{
DoStuffWithLocation();
break;
}
case LocationSettingsStatusCodes.ResolutionRequired:
{
try
{
// Show the dialog by calling startResolutionForResult(), and check the result
// in onActivityResult().
callback.Status.StartResolutionForResult(this, REQUEST_CHECK_SETTINGS);
}
catch (IntentSender.SendIntentException e)
{
}
break;
}
default:
{
// If all else fails, take the user to the android location settings
StartActivity(new Intent(Android.Provider.Settings.ActionLocationSourceSettings));
break;
}
}
});
}
protected override void OnActivityResult(int requestCode, Android.App.Result resultCode, Intent data)
{
switch (requestCode)
{
case REQUEST_CHECK_SETTINGS:
{
switch (resultCode)
{
case Android.App.Result.Ok:
{
DoStuffWithLocation();
break;
}
case Android.App.Result.Canceled:
{
//No location
break;
}
}
break;
}
}
}
NOTE:
This will not work with Huawei or other devices which does not have google services installed.
Kotlin Solution
Add build.gradle(Module:app)
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
after that create this function
fun enablegps() {
val mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(2000)
.setFastestInterval(1000)
val settingsBuilder = LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest)
settingsBuilder.setAlwaysShow(true)
val result = LocationServices.getSettingsClient(this).checkLocationSettings(settingsBuilder.build())
result.addOnCompleteListener { task ->
//getting the status code from exception
try {
task.getResult(ApiException::class.java)
} catch (ex: ApiException) {
when (ex.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> try {
Toast.makeText(this,"GPS IS OFF",Toast.LENGTH_SHORT).show()
// Show the dialog by calling startResolutionForResult(), and check the result
// in onActivityResult().
val resolvableApiException = ex as ResolvableApiException
resolvableApiException.startResolutionForResult(this,REQUEST_CHECK_SETTINGS
)
} catch (e: IntentSender.SendIntentException) {
Toast.makeText(this,"PendingIntent unable to execute request.",Toast.LENGTH_SHORT).show()
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
Toast.makeText(
this,
"Something is wrong in your GPS",
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
Android Marshmallow 6 supports runtime permission. Runtime permissions only work on Marshmallow and on pre-Marshmallow it still works the old way.
You can learn more about it in this Android Developer official Video:
https://www.youtube.com/watch?v=C8lUdPVSzDk
And requesting permission: http://developer.android.com/training/permissions/requesting.html
Thank you Mattia Maestrini for the Answer, I would like to add that using
compile 'com.google.android.gms:play-services-location:8.1.0'
would suffice. This prevents your app including unnecessary libraries and helps for keeping your method count low.
With recent Marshmallow update, even when the Location setting is turned on, your app will require to explicitly ask for permission. The recommended way to do this is to show the Permissions section of your app wherein the user can toggle the permission as required. The code snippet for doing this is as below:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Location Permission");
builder.setMessage("The app needs location permissions. Please grant this permission to continue using the features of the app.");
builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.setNegativeButton(android.R.string.no, null);
builder.show();
}
} else {
// do programatically as show in the other answer
}
And override the onRequestPermissionsResult method as below:
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "coarse location permission granted");
} else {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
}
}
Another approach is you can also use the SettingsApi to inquire which location provider(s) are enabled. If none is enabled, you can prompt a dialog to change the setting from within the app.
If you want to replace deprecated onActivityResult and startResolutionForResult in Fragment, see https://stackoverflow.com/a/65816646/2914140.
val checkLocationSettings =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
if (result.resultCode == RESULT_OK) {
// GPS is turned on in system settings.
}
}
...
val intentSenderRequest = IntentSenderRequest.Builder(resolvable.resolution).build()
checkLocationSettings.launch(intentSenderRequest)
private void openDeviceLocationRequest() {
if (!locationPermissionGranted)
return;
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
builder.setAlwaysShow(true); //this is the key ingredient
Task<LocationSettingsResponse> result =
LocationServices.getSettingsClient(this).checkLocationSettings(builder.build());
result.addOnCompleteListener(task -> {
try {
LocationSettingsResponse response = task.getResult(ApiException.class);
// All location settings are satisfied. The client can initialize location
// requests here.
if(lastKnownLocation == null)
getDeviceLocation();
} 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(
MapAddressActivity.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;
}
}
});
}
The simplest way I found while my research, is to create a Util class for this location requesting process and then call it to turn the gps ON for us.
Please check this blog! It told the whole story.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
LocationManager manager = (LocationManager) getSystemService( Context.LOCATION_SERVICE );
if (!manager.isProviderEnabled( LocationManager.GPS_PROVIDER )) {
buildAlertMessageNoGps();
}
}
private void buildAlertMessageNoGps() {
final AlertDialog.Builder builder = new AlertDialog.Builder( this );
builder.setMessage( "Your GPS seems to be disabled, do you want to enable it?" )
.setCancelable( false )
.setPositiveButton( "Yes", (dialog, id) -> startActivity( new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS ) ) )
.setNegativeButton( "No", (dialog, id) -> dialog.cancel() );
final AlertDialog alert = builder.create();
alert.show();
}

Categories

Resources