LocationSettingsRequest cuadro de diálogo para habilitar GPS – onActivityResult () omitido

Parte de mi aplicación requiere servicios de ubicación, por lo que si la ubicación está actualmente desactivada, la aplicación solicitará al usuario que la habilite. Así es como lo estoy haciendo: (También visto en esta respuesta de desbordamiento de stack)

LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(mLocationRequest); builder.setAlwaysShow(true); PendingResult result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build()); result.setResultCallback(new ResultCallback() { @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; } } 

Este código funciona bien, sin embargo, onActivityResult() siempre se omite. Si el usuario presiona Yes , No o back desde el onActivityResult() Dialog , onActivityResult() no se ejecuta.

Necesito Android para llamar a onActivityResult() así que si el usuario elige no activar los servicios de ubicación, puedo manejarlo adecuadamente.

La página del desarrollador de Google (y el código anterior) dice explícitamente que debe llamarse a onActivityResult() . ¿Alguien sabe por qué se salta?

Tampoco sé cuál es el propósito de esta línea:

final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);

¡Gracias!

Editar: información básica sobre la estructura de mi aplicación:

  • Este código está contenido en el método onResume() de un Fragment que implementa GoogleApiClient.ConnectionCallbacks , GoogleApiClient.OnConnectionFailedListener y LocationListener para recibir actualizaciones de ubicación. Ejemplo visto aquí .
  • En onLocationChanged() el Fragment tendrá una llamada de View personalizada invalidate() y volverá a dibujarse con la información actualizada.

Parece que el problema principal es que tienes todo el código en un Fragmento, y dado que startResolutionForResult() necesita una Actividad pasada a él, la Actividad es la que obtiene la callback onActivityResult() .

Una forma de onActivityResult() es usar la técnica que se describe aquí , llamar manualmente al método Fragment onActivityResult() de la Actividad cuando onActivityResult() el resultado.

Acabo de tener este sencillo ejemplo funcionando.

Primero, la Actividad, que agrega el Fragmento, y también tiene la funcionalidad para pasar el resultado de onActivityResult() al Fragmento:

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

Aquí está el Fragmento, que contiene toda la funcionalidad para mostrar el diálogo y manejar el resultado. En este simple ejemplo, acabo de utilizar los mensajes de Toast para verificar que funcionen como se esperaba. Tenga en cuenta que el principal cambio que he hecho aquí desde el código en su pregunta es el uso de getActivity() para obtener la referencia de actividad necesaria para la llamada a startResolutionForResult() .

 public class LocationFragment extends Fragment implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { LocationRequest mLocationRequest; GoogleApiClient mGoogleApiClient; PendingResult 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() { @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) { } } 

Aquí están los resultados visualmente, primero se muestra el cuadro de diálogo si el Modo de ubicación está deshabilitado:

enter image description here

Luego, si el usuario hace clic en No, el resultado se pasa de la Actividad al Fragmento, que muestra un Toast:

enter image description here

Lo mismo cuando el usuario hace clic en Sí, pero con un resultado exitoso, y el modo de ubicación está habilitado:

enter image description here

Tenga en cuenta que podría ser una mejor opción simplemente mantener toda esta funcionalidad en la Actividad, y luego llamar a un método público en el Fragmento cuando llegue el resultado.

Aquí está el código completamente funcional para mantener la funcionalidad en la Actividad. Por supuesto, en esta solución, necesitaría agregar una llamada al Fragmento para actualizar el estado del Modo Ubicación después de onActivityResult() .

 public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { LocationRequest mLocationRequest; GoogleApiClient mGoogleApiClient; PendingResult 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() { @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) { } } 

Debe agregar esto a la callback de resultado:

 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 será invocado en su fragmento, no necesita llamarlo manualmente en su actividad. Esto es esencialmente cómo funciona startResolutionForResult .

Veo que usa diferentes constantes REQUEST_CHECK_SETTINGS y REQUEST_LOCATION para el código de solicitud. ¿Tienen el mismo valor?

Para el código: final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent); .
El objective del código anterior es obtener el estado actual de la configuración de Ubicación (como usar Red, GPS, …) después de cambiar la configuración.
Además, en su código, creo que debería ser LocationSettingsStates.fromIntent(data); porque la intent no es exixst aquí, tal vez es solo un error tipográfico.

Es debido a todos los códigos google api presentes en los Fragmentos. Intenta lo siguiente que te ayudará a superar …

1.Cree un constructor vacío para sus fragmentos.

2.need el método oncreate () antes de onCreateView () …

3. Pegar el código de API de Google dentro de oncreate () ….

  public mainFragment(){ } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { buildGoogleApiClient(); buildLocationSettingsRequest(); checkLocationSettings(); mGoogleApiClient.connect(); } catch (Exception e) { e.printStackTrace(); } } 

Para tu referencia…

Haga clic aquí…

Guardar el campo de fragmento en la actividad (como sugirió Daniel) no suele ser una buena decisión, porque imagine que tiene múltiples fragmentos y cada uno contiene un código de ubicación. Lo hice de otra manera:

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

Código fuente

https://drive.google.com/open?id=0BzBKpZ4nzNzUOXM2eEhHM3hOZk0

Dependencia en el archivo build.gradle

compile ‘com.google.android.gms: play-services-location: 7.8.0’

Permiso en Archivo Manifiesto

     package com.keshav.enablelocationwithokcancelbuttoncontrol; import android.content.Context; import android.location.Address; import android.location.Geocoder; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.IOException; import java.util.List; import java.util.Locale; public class LocationAddress { private static final String TAG = "LocationAddress"; public static void getAddressFromLocation(final double latitude, final double longitude, final Context context, final Handler handler) { Thread thread = new Thread() { @Override public void run() { Geocoder geocoder = new Geocoder(context, Locale.getDefault()); String result = null; try { List
addressList = geocoder.getFromLocation( latitude, longitude, 1); if (addressList != null && addressList.size() > 0) { Address address = addressList.get(0); StringBuilder sb = new StringBuilder(); for (int i = 0; i < address.getMaxAddressLineIndex(); i++) { sb.append(address.getAddressLine(i)).append("\n"); } sb.append(address.getLocality()).append("\n"); sb.append(address.getPostalCode()).append("\n"); sb.append(address.getCountryName()); result = sb.toString(); } } catch (IOException e) { Log.e(TAG, "Unable connect to Geocoder", e); } finally { Message message = Message.obtain(); message.setTarget(handler); if (result != null) { message.what = 1; Bundle bundle = new Bundle(); result = "Latitude: " + latitude + " Longitude: " + longitude + "\n\nAddress:\n" + result; bundle.putString("address", result); message.setData(bundle); } else { message.what = 1; Bundle bundle = new Bundle(); result = "Latitude: " + latitude + " Longitude: " + longitude + "\n Unable to get address for this lat-long."; bundle.putString("address", result); message.setData(bundle); } message.sendToTarget(); } } }; thread.start(); } } package com.keshav.enablelocationwithokcancelbuttoncontrol; import android.app.AlertDialog; import android.app.Service; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.IBinder; import android.provider.Settings; import android.util.Log; public class GPSTracker extends Service implements LocationListener { private final Context mContext; // flag for GPS status boolean isGPSEnabled = false; // flag for network status boolean isNetworkEnabled = false; // flag for GPS status boolean canGetLocation = false; Location location; // location double latitude; // latitude double longitude; // longitude // The minimum distance to change Updates in meters private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters // The minimum time between updates in milliseconds private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute // Declaring a Location Manager protected LocationManager locationManager; public GPSTracker(Context context) { this.mContext = context; getLocation(); } public Location getLocation() { try { locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE); // getting GPS status isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); // getting network status isNetworkEnabled = locationManager .isProviderEnabled(LocationManager.NETWORK_PROVIDER); if (!isGPSEnabled && !isNetworkEnabled) { // no network provider is enabled } else { this.canGetLocation = true; // First get location from Network Provider if (isNetworkEnabled) { locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this); Log.d("Network", "Network"); if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); if (location != null) { latitude = location.getLatitude(); longitude = location.getLongitude(); } } } // if GPS Enabled get lat/long using GPS Services if (isGPSEnabled) { if (location == null) { locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this); Log.d("GPS Enabled", "GPS Enabled"); if (locationManager != null) { location = locationManager .getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location != null) { latitude = location.getLatitude(); longitude = location.getLongitude(); } } } } } } catch (Exception e) { e.printStackTrace(); } return location; } /** * Stop using GPS listener * Calling this function will stop using GPS in your app * */ public void stopUsingGPS(){ if(locationManager != null){ locationManager.removeUpdates(GPSTracker.this); } } /** * Function to get latitude * */ public double getLatitude(){ if(location != null){ latitude = location.getLatitude(); } // return latitude return latitude; } /** * Function to get longitude * */ public double getLongitude(){ if(location != null){ longitude = location.getLongitude(); } // return longitude return longitude; } /** * Function to check GPS/wifi enabled * @return boolean * */ public boolean canGetLocation() { return this.canGetLocation; } /** * Function to show settings alert dialog * On pressing Settings button will lauch Settings Options * */ public void showSettingsAlert(){ AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext); // Setting Dialog Title alertDialog.setTitle("GPS is settings"); // Setting Dialog Message alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?"); // On pressing Settings button alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); mContext.startActivity(intent); } }); // on pressing cancel button alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); // Showing Alert Message alertDialog.show(); } @Override public void onLocationChanged(Location location) { } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public IBinder onBind(Intent arg0) { return null; } } package com.keshav.enablelocationwithokcancelbuttoncontrol; import android.content.Context; import android.location.Address; import android.location.Geocoder; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.IOException; import java.util.List; import java.util.Locale; public class LocationAddress { private static final String TAG = "LocationAddress"; public static void getAddressFromLocation(final double latitude, final double longitude, final Context context, final Handler handler) { Thread thread = new Thread() { @Override public void run() { Geocoder geocoder = new Geocoder(context, Locale.getDefault()); String result = null; try { List
addressList = geocoder.getFromLocation( latitude, longitude, 1); if (addressList != null && addressList.size() > 0) { Address address = addressList.get(0); StringBuilder sb = new StringBuilder(); for (int i = 0; i < address.getMaxAddressLineIndex(); i++) { sb.append(address.getAddressLine(i)).append("\n"); } sb.append(address.getLocality()).append("\n"); sb.append(address.getPostalCode()).append("\n"); sb.append(address.getCountryName()); result = sb.toString(); } } catch (IOException e) { Log.e(TAG, "Unable connect to Geocoder", e); } finally { Message message = Message.obtain(); message.setTarget(handler); if (result != null) { message.what = 1; Bundle bundle = new Bundle(); result = "Latitude: " + latitude + " Longitude: " + longitude + "\n\nAddress:\n" + result; bundle.putString("address", result); message.setData(bundle); } else { message.what = 1; Bundle bundle = new Bundle(); result = "Latitude: " + latitude + " Longitude: " + longitude + "\n Unable to get address for this lat-long."; bundle.putString("address", result); message.setData(bundle); } message.sendToTarget(); } } }; thread.start(); } }

Si quieres resultados en tu fragmento que usar

 startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null); 

en lugar de status.startResolutionForResult(YourActivity, LOCATION_REQUEST);

USANDO el método anterior entregará el resultado de vuelta a su fragmento solamente.