启用 GPS 的 LocationSettingsRequest 对话框 - 跳过 onActivityResult()
LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped
我的应用程序的一部分需要位置服务,因此如果当前关闭位置,应用程序将提示用户启用它。 Here is how I am doing it: (Also seen in 堆栈溢出答案)
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
或 back
来自 Dialog
,onActivityResult()
不会 运行。
我需要Android来调用onActivityResult()
所以如果用户选择不开启位置服务,我可以适当处理。
Google 的开发者页面(和上面的代码)明确指出应该调用 onActivityResult()
。有人知道为什么它被跳过了吗?
我也不知道这行的目的是什么:
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
谢谢!
编辑:关于我的应用结构的基本信息:
- 此代码包含在
Fragment
的 onResume()
方法中,该方法实现 GoogleApiClient.ConnectionCallbacks
、GoogleApiClient.OnConnectionFailedListener
和 LocationListener
以接收位置更新。看到的示例 here.
- 在
onLocationChanged()
中,Fragment
将有一个自定义 View
调用 invalidate()
并使用更新的信息重新绘制自身。
我看到您对请求代码使用了不同的常量 REQUEST_CHECK_SETTINGS
和 REQUEST_LOCATION
。他们有相同的价值吗?
对于代码:final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);
。
上述代码的目的是在更改设置后获取位置设置(如使用网络、GPS 等)的当前状态。
另外,在你的代码中,我认为它应该是 LocationSettingsStates.fromIntent(data);
因为 intent
在这里不存在,也许这只是一个错字。
更新
下面的原始答案使用的是 Java 和现已弃用的 SettingsApi。
这是一种使用 Kotlin 和 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) { }
}
}
}
}
在MainActivity中,定义常量:
companion object {
const val LOCATION_SETTING_REQUEST = 999
}
原始答案:
看起来主要问题是您将所有代码都放在一个片段中,并且由于 startResolutionForResult()
需要将 Activity 传递给它,所以 Activity 就是得到的onActivityResult()
回调。
解决这个问题的一种方法是使用 here 中描述的技术,在结果出现时从 Activity 手动调用 Fragment 的 onActivityResult()
方法。
我刚刚让这个简单的例子开始工作。
首先,Activity,它添加了 Fragment,并且还具有将 onActivityResult()
的结果传递给 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);
}
}
}
这是片段,它包含显示对话框和处理结果的所有功能。在这个简单的示例中,我只是使用 Toast 消息来验证它是否按预期工作。请注意,我在这里对您问题中的代码所做的主要更改是使用 getActivity()
来获取调用 startResolutionForResult()
.[=28= 所需的 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) {
}
}
这是直观的结果,如果禁用定位模式,首先会显示对话框:
然后,如果用户单击“否”,结果会从 Activity 传递到显示 Toast 的 Fragment:
当用户单击“是”时发生同样的事情,但结果是成功的,并且启用了定位模式:
请注意,将所有这些功能保留在 Activity 中可能是更好的选择,然后在结果出现时调用 Fragment 中的 public 方法。
这里是保留 Activity 功能的完整代码。
当然,在此解决方案中,您需要在片段中添加调用以在调用 onActivityResult()
后更新位置模式的状态。
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) {
}
}
这是因为片段中存在所有 google api 代码。尝试以下将有助于克服...
1.Create 一个用于片段的空构造函数。
2.need onCreateView() 之前的 oncreate() 方法...
3.paste 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();
}
}
For your Reference...
您需要将此添加到结果回调中:
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
将在您的片段上调用,您无需在 activity 中手动调用它。这就是 startResolutionForResult
的工作原理。
在 activity 中保存片段字段(正如 Daniel 所建议的)通常不是一个好的决定,因为假设您有多个片段并且每个片段都包含位置代码。我以不同的方式做到了:
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();
}
}
如果你想将结果返回到你的片段而不是使用
startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
而不是status.startResolutionForResult(YourActivity, LOCATION_REQUEST);
使用上述方法只会将结果返回给您的片段。
用于处理来自以下片段的启用位置是可以使用的最新代码。设置 API 现已弃用。下面是SettingsClient的使用方法API.
我还注意到,在 Android 10 台设备中,即使用户启用了位置; onActivityResult 中的状态结果为 RESULT_CANCELED,我无法在 Android 10 设备中找到解决该问题的方法,而在 Android PIE 中,结果代码为 RESULT_OK.因此,检测用户是否启用它的唯一方法是使用 LocationManagerCompat API 明确检查是否启用了位置 Android 10 台设备
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")
}
}
}
}
感谢@gianlucaparadise solution 你应该写新的 API:
片段(或者 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.
}
}
}
要启用 GPS 的片段或实用程序 class(参见 or ):
.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.")
}
已弃用 Fragment 和 onActivityResult
的 API 变体:。
当您需要解析 Status
或 ResolvableApiException
时,我建议您利用 activity.registerForActivityResult
API 代替 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);
您正在使用 Java,但如果需要 Kotlin:
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)
对于 Kotlin 用户
此解决方案适用于 Activity
和 Fragment
,方法是在 checkLocationSetting()
中进行以下更改:
为ActivityresolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)
对于片段
startIntentSenderForResult(resolvableApiException.resolution.intentSender, REQUEST_CHECK_SETTING, null, 0, 0,0,null)
通过使用LocationSettingsResponse可以完成这个任务。
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
}
}
}
}
}
上Activity结果
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 完成代码 MainActivity.kt
输出:
Link 完成代码 MainActivity.kt
我的应用程序的一部分需要位置服务,因此如果当前关闭位置,应用程序将提示用户启用它。 Here is how I am doing it: (Also seen in
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
或 back
来自 Dialog
,onActivityResult()
不会 运行。
我需要Android来调用onActivityResult()
所以如果用户选择不开启位置服务,我可以适当处理。
Google 的开发者页面(和上面的代码)明确指出应该调用 onActivityResult()
。有人知道为什么它被跳过了吗?
我也不知道这行的目的是什么:
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
谢谢!
编辑:关于我的应用结构的基本信息:
- 此代码包含在
Fragment
的onResume()
方法中,该方法实现GoogleApiClient.ConnectionCallbacks
、GoogleApiClient.OnConnectionFailedListener
和LocationListener
以接收位置更新。看到的示例 here. - 在
onLocationChanged()
中,Fragment
将有一个自定义View
调用invalidate()
并使用更新的信息重新绘制自身。
我看到您对请求代码使用了不同的常量 REQUEST_CHECK_SETTINGS
和 REQUEST_LOCATION
。他们有相同的价值吗?
对于代码:final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);
。
上述代码的目的是在更改设置后获取位置设置(如使用网络、GPS 等)的当前状态。
另外,在你的代码中,我认为它应该是 LocationSettingsStates.fromIntent(data);
因为 intent
在这里不存在,也许这只是一个错字。
更新
下面的原始答案使用的是 Java 和现已弃用的 SettingsApi。
这是一种使用 Kotlin 和 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) { }
}
}
}
}
在MainActivity中,定义常量:
companion object {
const val LOCATION_SETTING_REQUEST = 999
}
原始答案:
看起来主要问题是您将所有代码都放在一个片段中,并且由于 startResolutionForResult()
需要将 Activity 传递给它,所以 Activity 就是得到的onActivityResult()
回调。
解决这个问题的一种方法是使用 here 中描述的技术,在结果出现时从 Activity 手动调用 Fragment 的 onActivityResult()
方法。
我刚刚让这个简单的例子开始工作。
首先,Activity,它添加了 Fragment,并且还具有将 onActivityResult()
的结果传递给 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);
}
}
}
这是片段,它包含显示对话框和处理结果的所有功能。在这个简单的示例中,我只是使用 Toast 消息来验证它是否按预期工作。请注意,我在这里对您问题中的代码所做的主要更改是使用 getActivity()
来获取调用 startResolutionForResult()
.[=28= 所需的 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) {
}
}
这是直观的结果,如果禁用定位模式,首先会显示对话框:
然后,如果用户单击“否”,结果会从 Activity 传递到显示 Toast 的 Fragment:
当用户单击“是”时发生同样的事情,但结果是成功的,并且启用了定位模式:
请注意,将所有这些功能保留在 Activity 中可能是更好的选择,然后在结果出现时调用 Fragment 中的 public 方法。
这里是保留 Activity 功能的完整代码。
当然,在此解决方案中,您需要在片段中添加调用以在调用 onActivityResult()
后更新位置模式的状态。
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) {
}
}
这是因为片段中存在所有 google api 代码。尝试以下将有助于克服...
1.Create 一个用于片段的空构造函数。
2.need onCreateView() 之前的 oncreate() 方法...
3.paste 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();
}
}
For your Reference...
您需要将此添加到结果回调中:
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
将在您的片段上调用,您无需在 activity 中手动调用它。这就是 startResolutionForResult
的工作原理。
在 activity 中保存片段字段(正如 Daniel 所建议的)通常不是一个好的决定,因为假设您有多个片段并且每个片段都包含位置代码。我以不同的方式做到了:
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();
}
}
如果你想将结果返回到你的片段而不是使用
startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
而不是status.startResolutionForResult(YourActivity, LOCATION_REQUEST);
使用上述方法只会将结果返回给您的片段。
用于处理来自以下片段的启用位置是可以使用的最新代码。设置 API 现已弃用。下面是SettingsClient的使用方法API.
我还注意到,在 Android 10 台设备中,即使用户启用了位置; onActivityResult 中的状态结果为 RESULT_CANCELED,我无法在 Android 10 设备中找到解决该问题的方法,而在 Android PIE 中,结果代码为 RESULT_OK.因此,检测用户是否启用它的唯一方法是使用 LocationManagerCompat API 明确检查是否启用了位置 Android 10 台设备
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")
}
}
}
}
感谢@gianlucaparadise solution 你应该写新的 API:
片段(或者 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.
}
}
}
要启用 GPS 的片段或实用程序 class(参见
.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.")
}
已弃用 Fragment 和 onActivityResult
的 API 变体:
当您需要解析 Status
或 ResolvableApiException
时,我建议您利用 activity.registerForActivityResult
API 代替 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);
您正在使用 Java,但如果需要 Kotlin:
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)
对于 Kotlin 用户
此解决方案适用于 Activity
和 Fragment
,方法是在 checkLocationSetting()
中进行以下更改:
为ActivityresolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)
对于片段
startIntentSenderForResult(resolvableApiException.resolution.intentSender, REQUEST_CHECK_SETTING, null, 0, 0,0,null)
通过使用LocationSettingsResponse可以完成这个任务。
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
}
}
}
}
}
上Activity结果
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 完成代码 MainActivity.kt
输出:
Link 完成代码 MainActivity.kt