LocationSettingsRequest对话框启用GPS – onActivityResult()跳过
我的应用程序的一部分需要位置服务,所以如果位置当前closures,应用程序将提示用户启用它。 这是我如何做到这一点:(也见于这个堆栈溢出答案)
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; } }
这个代码运行良好,但是, onActivityResult()
总是被跳过。 无论用户是否按下“ Yes
,“ No
”或从Dialog
back
, onActivityResult()
都不会运行。
我需要Android调用onActivityResult()
所以如果用户select不打开位置服务,我可以适当地处理它。
Google的开发者页面(以及上面的代码)明确指出应该调用onActivityResult()
。 任何人都知道为什么它被跳过?
我也不知道这一行的目的是什么:
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
谢谢!
编辑:我的应用程序的结构的基本信息:
- 此代码包含在
Fragment
的onResume()
方法中,该方法实现了GoogleApiClient.ConnectionCallbacks
,GoogleApiClient.OnConnectionFailedListener
和LocationListener
来接收位置更新。 例子在这里看到。 - 在
onLocationChanged()
Fragment
将有一个自定义View
调用invalidate()
并重新绘制自己的更新信息。
看起来主要的问题在于你拥有Fragment中的所有代码,而且由于startResolutionForResult()
需要一个传递给它的Activity,Activity就是获得onActivityResult()
callback的东西。
解决这个问题的一个方法就是使用这里描述的技术,当结果出来时,从Activity中手动调用Fragment的onActivityResult()
方法。
我刚刚得到这个简单的例子工作。
首先,Activity添加了Fragment,并且还具有将onActivityResult()
的结果传递给Fragment的function:
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); } } }
这里是片段,其中包含显示对话框的所有function,并处理结果。 在这个简单的例子中,我只是使用Toast消息来validation它是否按预期工作。 请注意,我在这里从代码中所做的主要更改是使用getActivity()
获取调用startResolutionForResult()
所需的Activity引用。
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) { } }
以下是视觉效果,首先如果位置模式被禁用,则显示对话框:
然后,如果用户单击“否”,则结果将从“活动”传递到“片段”,该片段将显示Toast:
当用户点击是时,同样的事情,但成功的结果,并启用位置模式:
请注意,在Activity中保留所有这些function可能是一个更好的select,然后在结果进入时调用Fragment中的公共方法。
这里是完整的工作代码,以保持活动中的function。 当然,在这个解决scheme中,您需要在调用onActivityResult()
之后向Fragment中添加一个调用来更新位置模式的状态。
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) { } }
您需要将此添加到您的结果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
将在您的片段上被调用,您不需要在您的活动中手动调用它。 这基本上是startResolutionForResult
工作原理。
我看到你使用不同的常量REQUEST_CHECK_SETTINGS
和REQUEST_LOCATION
来请求代码。 他们有相同的价值吗?
对于代码: final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);
。
以上代码的目的是在更改设置后获取位置设置的当前状态(如使用networking,GPS,…)。
另外,在你的代码中,我认为它应该是LocationSettingsStates.fromIntent(data);
因为intent
不在这里exixst,也许这只是一个错字。
它的所有谷歌api代码目前在碎片..尝试下面,它将有助于克服…
1.为您的碎片创build一个空的构造函数。
2. onCreateView()之前需要oncreate()方法…
3.在oncreate()中面糊Google api代码
public mainFragment(){ } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { buildGoogleApiClient(); buildLocationSettingsRequest(); checkLocationSettings(); mGoogleApiClient.connect(); } catch (Exception e) { e.printStackTrace(); } }
供你参考…
点击这里…
在活动中保存片段字段(正如丹尼尔所build议的)通常不是一个好的决定,因为想象你有多个片段,每个片段都包含位置代码。 我以不同的方式做了:
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(); } } }
源代码
https://drive.google.com/open?id=0BzBKpZ4nzNzUOXM2eEhHM3hOZk0
依赖于build.gradle文件
编译“com.google.android.gms:play-services-location:7.8.0”
清单文件中的权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 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<Address> 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<Address> 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(); } }