Place Autocomplete how to do this right - android

I'm using following code
try {
Intent intent =
new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_OVERLAY)
.build(this);
startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
} catch (GooglePlayServicesRepairableException e) {
// TODO: Handle the error.
} catch (GooglePlayServicesNotAvailableException e) {
// TODO: Handle the error.
}
and
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Place place = PlaceAutocomplete.getPlace(this, data);
Log.i(TAG, "Place: " + place.getName());
} else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
Status status = PlaceAutocomplete.getStatus(this, data);
// TODO: Handle the error.
Log.i(TAG, status.getStatusMessage());
} else if (resultCode == RESULT_CANCELED) {
// The user canceled the operation.
}
}
}
but dont see anythink what i want, so i wanna ask how to do some kind of listener on my EditText wich use PlaceAutocomplete to search for location, it should look like My EditText and under is my map, when I put K it will show all location starting at K under my EditText and I could choose it and marker location with camera smooth move

Well it could be done with
Intent intent = new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_OVERLAY)
.zzih(searchString)
.build(this);
Notice zzih method, it allows you to pass searchString to PlaceAutocomplete. Also in different versions of google services it can have another name.
The problem is that PlaceAutocomplete overlays entire screen so you cannot add your EditText over it.
When I faced same problem I had to implement UI by my self and use Google Places Web API because some features do not exists in Google Places Android API.
But you can try to use GeoDataApi.getAutocompletePredictions().
For using GeoDataApi.getAutocompletePredictions() you should:
Create field in your Fragment/Activity
private GoogleApiClient mGoogleApiClient;
Instantiate it and manage its lifecycle
#Override
protected void onCreate( Bundle savedInstanceState ) {
mGoogleApiClient = new GoogleApiClient
.Builder( this )
.enableAutoManage( this, 0, this )
.addApi( Places.GEO_DATA_API )
.addApi( Places.PLACE_DETECTION_API )
.addConnectionCallbacks( this )
.addOnConnectionFailedListener( this )
.build();
}
#Override
protected void onStart() {
super.onStart();
if( mGoogleApiClient != null )
mGoogleApiClient.connect();
}
#Override
protected void onStop() {
if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
Create filter, list of available filters is here
AutocompleteFilter typeFilter = new AutocompleteFilter.Builder()
.setTypeFilter(AutocompleteFilter.TYPE_FILTER_ADDRESS)
.build();
Set bounds. Notice that first coordinate is southwest, seconds is northeast.
LatLngBounds bounds = new LatLngBounds(new LatLng(39.906374, -105.122337), new LatLng(39.949552, -105.068779));
Search for autocomplete prediction
Places.GeoDataApi.getAutocompletePredictions(mGoogleApiClient, "my street",
SharedInstances.session().getCity().getBounds(), typeFilter).setResultCallback(new ResultCallback<AutocompletePredictionBuffer>() {
#Override
public void onResult(#NonNull AutocompletePredictionBuffer buffer) {
if( buffer == null )
return;
if( buffer.getStatus().isSuccess() ) {
for( AutocompletePrediction prediction : buffer ) {
Log.d(TAG,"Prediction placeId "+prediction.getPlaceId());
Log.d(TAG,"Prediction Primary Text "+prediction.getPrimaryText(null));
Log.d(TAG,"Prediction Secondary Text "+prediction.getSecondaryText(null));
}
//Prevent memory leak by releasing buffer
buffer.release();
}
});
Notice that AutocompletePrediction does not contain any information about coordinates. So if you need it you have to request Place object by placeId.
Places.GeoDataApi.getPlaceById( mGoogleApiClient, googlePlaceId).setResultCallback( new ResultCallback<PlaceBuffer>() {
#Override
public void onResult(PlaceBuffer places) {
if( places.getStatus().isSuccess() ) {
Place place = places.get( 0 );
}
//Release the PlaceBuffer to prevent a memory leak
places.release();
}});
I guess paragraphs 3. and 4. are not necessary so you can pass null instead.

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

How can i clear text of selected Place name from AutocompleteSupportFragment?

i create my project for ride booking app. I use AutocompleteSupportFragment for search place . After selecting Place , i filter that location if you are in Delhi then set Text of AutocompleteSupportFragment other wise set Hint like "Please select valid place".
I try
autocompleteFragment_to.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(#NonNull Place place) {
if (!getStateName(place.getLatLng().latitude, place.getLatLng().longitude).equals("Madhya Pradesh")) {
Common.myLatLong = null;
Common.placeName1 = null;
Common.placeName1City = null;
Log.e("FRagmentData",""+autocompleteFragment_to.a.getText().toString());
autocompleteFragment_to.a.getText().toString();
autocompleteFragment_to.a.setText("");
autocompleteFragment_to.a.setHint("Please select Valid Place");
autocompleteFragment_to.getView().findViewById(R.id.places_autocomplete_clear_button).performClick();
autocompleteFragment_to.a.setText("");
Toast.makeText(context, "Please select Valid Place", Toast.LENGTH_SHORT).show();
} else {
destination = place.getLatLng();
Common.myLatLong = place.getLatLng();
Common.placeName1 = place.getName();
Log.e("FRagmentData",""+autocompleteFragment_to.a.getText().toString());
Common.placeName1City = getAddress(context, place.getLatLng().latitude, place.getLatLng().longitude);
if (mMap != null) {
mMap.clear();
}
mapFragment.getMapAsync(MainActivity.this);
}
}
#Override
public void onError(#NonNull Status status) {
Toast.makeText(context, "try again later", Toast.LENGTH_SHORT).show();
}
});
I try all these approch but it's not working
Use AutoComplete IntentBuilder instead of fragment..
Just initialise Places inside oncreate()
// Initialize Places.
Places.initialize(getApplicationContext(), "***YOUR API KEY***");
// Create a new Places client instance.
PlacesClient placesClient = Places.createClient(this);
then call intentbuilder in textview.onclick()
List<Place.Field> fields = Arrays.asList(Place.Field.ID, Place.Field.NAME);
// Start the autocomplete intent.
Intent intent = new Autocomplete.IntentBuilder(
AutocompleteActivityMode.FULLSCREEN, fields)
.build(this);
startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE);
onActivity result you will get selected place.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Place place = Autocomplete.getPlaceFromIntent(data);
Log.i(TAG, "Place: " + place.getName() + ", " + place.getId());
} else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
// TODO: Handle the error.
Status status = Autocomplete.getStatusFromIntent(data);
Log.i(TAG, status.getStatusMessage());
} else if (resultCode == RESULT_CANCELED) {
// The user canceled the operation.
}
}
After that, just set the selected place in the Textview.
Textview.setText(place.getName());
refer:https://stackoverflow.com/a/55045772/10579969

Android Google Drive Integration - Drive not connecting

I am new to android and trying to use Google Drive to store and retrieve data.I have write a code to connect google drive. It is showing account chooser dialog and on selecting of account nothing is happening.
public class SyncActivity extends MainActivity implements GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener
{
GoogleApiClient googleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sync);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addScope(Drive.SCOPE_APPFOLDER)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
Button btnConnectDrive = (Button)findViewById(R.id.connectDrive);
btnConnectDrive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try{
googleApiClient.connect();
}
catch(Exception e){
e.printStackTrace();
}
}
});
}
#Override
public void onConnected(Bundle bundle) {
super.onConnected(bundle);
System.out.println("Connected!!!!!!!!!!!!!!!");
}
#Override
public void onConnectionSuspended(int i) {
super.onConnectionSuspended(i);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
super.onConnectionFailed(connectionResult);
if(connectionResult.hasResolution()){
try {
connectionResult.startResolutionForResult(this, ConnectionResult.RESOLUTION_REQUIRED);
} catch (IntentSender.SendIntentException e) {
// Unable to resolve, message user appropriately
e.printStackTrace();
}
}
else {
GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
}
}
}
There's a bunch of steps you need to follow like setup SHA1 fingerprint and obtaining credentials from your Google Dev Console. Follow the Steps indicated in Android Quickstart, the code snippets are provided too.
If you want a quick code reference, download the Android Demo for Drive API. I was able to run it on my Android device.
Here's a snippet on the Account chooser:
/**
* Called when an activity launched here (specifically, AccountPicker
* and authorization) exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
* #param requestCode code indicating which activity result is incoming.
* #param resultCode code indicating the result of the incoming
* activity result.
* #param data Intent (containing result data) returned by incoming
* activity result.
*/
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
mOutputText.setText(
"This app requires Google Play Services. Please install " +
"Google Play Services on your device and relaunch this app.");
} else {
getResultsFromApi();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
getResultsFromApi();
}
break;
}
}

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

LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped

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

Categories

Resources