启用 GPS 的 LocationSettingsRequest 对话框 - 跳过了 onActivityResult()
Posted
技术标签:
【中文标题】启用 GPS 的 LocationSettingsRequest 对话框 - 跳过了 onActivityResult()【英文标题】:LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped 【发布时间】:2015-09-22 23:49:44 【问题描述】:我的部分应用需要定位服务,所以如果定位当前处于关闭状态,应用会提示用户启用它。 Here 是我的做法:(也见于this Stack Overflow 答案)
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()
总是被跳过。无论用户是否从Dialog
按下Yes
、No
或back
,onActivityResult()
都不会运行。
我需要android调用onActivityResult()
所以如果用户选择不开启定位服务,我可以适当的处理。
Google 的开发者页面(以及上面的代码)明确指出应该调用 onActivityResult()
。有谁知道它为什么被跳过?
我也不知道这行的目的是什么:
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
谢谢!
编辑:关于我的应用程序结构的基本信息:
此代码包含在Fragment
的onResume()
方法中,该方法实现GoogleApiClient.ConnectionCallbacks
、GoogleApiClient.OnConnectionFailedListener
和LocationListener
以接收位置更新。示例见here。
在 onLocationChanged()
中,Fragment
将有一个自定义的 View
调用 invalidate()
并使用更新的信息重新绘制自身。
【问题讨论】:
【参考方案1】:更新
下面的原始答案是使用 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
原始答案:
看起来主要问题是您将所有代码都放在了 Fragment 中,并且由于 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()
所需的活动参考。
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 传递到 Fragment,其中显示一个 Toast:
当用户点击是,但结果是成功,并且启用了定位模式时,情况相同:
请注意,最好将所有这些功能保留在 Activity 中,然后在结果到来时调用 Fragment 中的公共方法。
这是用于在 Activity 中保留功能的完整工作代码。
当然在这个解决方案中,你需要在调用onActivityResult()
之后,在Fragment中添加一个调用来更新Location Mode的状态。
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)
【讨论】:
如何在ActivityResult上刷新位置 一旦用户启用了位置服务,您将需要请求位置更新。见这里:***.com/questions/34582370/… 嗨@DanielNugent 感谢您的帖子。我有一种情况,每次单击按钮时,我都会检查用户的设置是否符合预期。这只是意味着,我每次都调用 result.setResultCallback 来检查用户是否更改了设置。奇怪的是,这只适用于第一次。 onResult 永远不会被调用。我对同一个 ***.com/questions/38151650/… 有一个 SO 问题。你能帮忙吗 6.0 设备的最佳答案之一。但是,我在另一个片段中调用 LocationFragment 而根本没有得到 Toast。你能告诉我问题吗? 每次都返回0【参考方案2】:您需要将此添加到您的结果回调中:
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
的工作原理。
【讨论】:
请在您的内联代码位周围添加反引号包装器 我评论了这一行:status.startResolutionForResult(hostActivity, REQUEST_CHECK_SETTINGS);并使用您的代码及其工作,而无需在主要活动中覆盖 onActiivtyResult。 这应该是公认的答案。完美运行。 您也可以跳转到 Status.startResolutionForResult 的源代码并验证它是否正在执行此操作。不过,对 Status.hasResolution 也进行同样的检查可能是个好主意。 我们可以从前台服务调用 LocationSettingRequest 吗?因为我正在从前台服务请求位置更新。【参考方案3】:当您需要解析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)
【讨论】:
正是我正在搜索的内容,如何为此利用活动结果 API,因此整个位置获取、权限检查、设置都可以放入一个扩展函数中,我只需从任何需要的地方调用它。 .不用大惊小怪 谢谢兄弟,你节省了我的时间 现在应该标记为答案,旧标记的答案太过时了。这是目前最新的方法。【参考方案4】:如果您希望结果返回到您的片段而不是使用
startIntentSenderForResult(status.getResolution().getIntentSender(), REQUEST_CODE_LOCATION_SETTING, null, 0, 0, 0, null);
而不是status.startResolutionForResult(YourActivity, LOCATION_REQUEST);
使用上述方法只会将结果返回到您的片段。
【讨论】:
谢谢,但这是 Saša Tarbuk 回答 (***.com/a/39579124/2914140) 的副本。 我们可以从前台服务调用 LocationSettingRequest 吗?因为我正在从前台服务请求位置更新。 @user3410835 不,你不能。 @Ghodasara Bhaumik。谢谢。原因?? @user3410835 因为要启用位置,android 必须显示对话框并显示对话框,它需要您的应用程序的上下文。如果应用程序有前台服务正在运行,则意味着应用程序未启动并且 android 无法找到您的应用程序的上下文以显示位置启用对话框。【参考方案5】:用于处理以下片段中的启用位置是可以使用的最新代码。设置 API 现已弃用。以下是 SettingsClient API 的使用方法。
我还注意到,在 Android 10 设备中,即使用户启用了位置; onActivityResult 中的状态结果以 RESULT_CANCELED 出现,我无法在 Android 10 设备中找到解决该问题的方法,而在 Android PIE 中,结果代码为 RESULT_OK。因此,检测用户是否启用它的唯一方法是使用 Android 10 设备的 LocationManagerCompat API 显式检查是否启用了位置
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")
【讨论】:
【参考方案6】:感谢@gianlucaparadise solution,您应该为新 API 编写代码:
片段(或者可能是活动):
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 的片段或实用程序类(请参阅 1 或 2):
.addOnFailureListener(context) e ->
when ((e as? ApiException)?.statusCode)
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
try
// Cast to a resolvable exception.
val resolvable = e as ResolvableApiException
// Old API: show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
// New API: call registerForActivityResult::launch
// and check the result in callback.
val intentSenderRequest =
IntentSenderRequest.Builder(resolvable.resolution).build()
checkLocationSettings.launch(intentSenderRequest)
catch (sie: IntentSender.SendIntentException)
Timber.e("GPS: Unable to execute request.")
catch (cce: java.lang.ClassCastException)
// Ignore, should be an impossible error.
Timber.e("GPS: Unable to execute request, ClassCastException.")
已弃用 Fragment 和 onActivityResult
的 API 变体:LocationSettingsRequest dialog to enable GPS - onActivityResult() skipped。
【讨论】:
任何说startResolutionForResult
是旧的和registerForActivityResult
是新的来源?
@Dr.jacky,对不起,我现在没有这个项目。我使用了startResolutionForResult
和registerForActivityResult
,第一个没有导致片段中的结果或被弃用(我不记得了)。
@Dr.jacky,请参阅***.com/questions/67983163/…,developer.android.com/jetpack/androidx/releases/…。我的意思是片段,在活动中startResolutionForResult
仍在使用(developer.android.com/reference/android/app/…。onActivityResult
也已弃用。【参考方案7】:
我看到您对请求代码使用了不同的常量 REQUEST_CHECK_SETTINGS
和 REQUEST_LOCATION
。它们具有相同的价值吗?
代码:final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent);
。
上述代码的目的是在更改设置后获取位置设置的当前状态(如使用网络、GPS、...)。
另外,在您的代码中,我认为它应该是 LocationSettingsStates.fromIntent(data);
,因为 intent
在这里不存在,也许这只是一个错字。
【讨论】:
谢谢。对于这个问题,我直接从谷歌的开发者页面复制了代码。我的应用程序中不存在这些错误,但无论如何它们都不是问题。如果您现在查看onActivityResult()
,您会看到一个从未调用过的Log
函数。【参考方案8】:
这是因为片段中存在所有 google api 代码。尝试以下操作将有助于克服...
1.为您的片段创建一个空的构造函数。
2.在onCreateView()之前需要oncreate()方法...
3. 将 Google api 代码粘贴到 oncreate()....
public mainFragment()
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
try
buildGoogleApiClient();
buildLocationSettingsRequest();
checkLocationSettings();
mGoogleApiClient.connect();
catch (Exception e)
e.printStackTrace();
供您参考...
Click here...
【讨论】:
【参考方案9】:在活动中保存片段字段(如丹尼尔建议的那样)通常不是一个好的决定,因为假设您有多个片段并且每个片段都包含位置代码。我以不同的方式做到了:
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();
【讨论】:
【参考方案10】:源代码
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();
【讨论】:
【参考方案11】:对于 Kotlin 用户
此解决方案适用于Activity
和Fragment
,只需对checkLocationSetting()
进行以下更改:
关于活动 resolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)
片段
startIntentSenderForResult(resolvableApiException.resolution.intentSender, REQUEST_CHECK_SETTING, null, 0, 0,0,null)
使用LocationSettingsResponse可以完成这个任务。
在MainActivity.kt内
private fun checkLocationSetting()
locationRequest = LocationRequest.create()
locationRequest.apply
priority=LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 5000
fastestInterval = 2000
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
builder.setAlwaysShow(true)
val result: Task<LocationSettingsResponse> = LocationServices.getSettingsClient(applicationContext)
.checkLocationSettings(builder.build())
result.addOnCompleteListener
try
val response: LocationSettingsResponse = it.getResult(ApiException::class.java)
Toast.makeText(this@MainActivity, "GPS is On", Toast.LENGTH_SHORT).show()
Log.d(TAG, "checkSetting: GPS On")
catch(e:ApiException)
when(e.statusCode)
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
val resolvableApiException = e as ResolvableApiException
resolvableApiException.startResolutionForResult(this@MainActivity, REQUEST_CHECK_SETTING)
Log.d(TAG, "checkSetting: RESOLUTION_REQUIRED")
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE ->
// USER DEVICE DOES NOT HAVE LOCATION OPTION
onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
super.onActivityResult(requestCode, resultCode, data)
when(requestCode)
REQUEST_CHECK_SETTING ->
when(resultCode)
Activity.RESULT_OK->
Toast.makeText(this@MainActivity, "GPS is Turned on", Toast.LENGTH_SHORT).show()
Activity.RESULT_CANCELED ->
Toast.makeText(this@MainActivity, "GPS is Required to use this app", Toast.LENGTH_SHORT).show()
完整代码链接MainActivity.kt
输出:
链接到完整代码MainActivity.kt
【讨论】:
REQUEST_CHECK_SETTING 没有为我导入... 这是一个常数。你可以自己声明。 这里是 mainActivity.java 的链接:gist.github.com/ShoaibKakal/4d07b4001e34cb770d3ee7cae294f7b0以上是关于启用 GPS 的 LocationSettingsRequest 对话框 - 跳过了 onActivityResult()的主要内容,如果未能解决你的问题,请参考以下文章