启用 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 按下YesNobackonActivityResult() 都不会运行。

我需要android调用onActivityResult()所以如果用户选择不开启定位服务,我可以适当的处理。

Google 的开发者页面(以及上面的代码)明确指出应该调用 onActivityResult()。有谁知道它为什么被跳过?

我也不知道这行的目的是什么:

final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);

谢谢!

编辑:关于我的应用程序结构的基本信息:

此代码包含在FragmentonResume() 方法中,该方法实现GoogleApiClient.ConnectionCallbacksGoogleApiClient.OnConnectionFailedListenerLocationListener 以接收位置更新。示例见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】:

当您需要解析StatusResolvableApiException 时,我建议您利用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,对不起,我现在没有这个项目。我使用了startResolutionForResultregisterForActivityResult,第一个没有导致片段中的结果或被弃用(我不记得了)。 @Dr.jacky,请参阅***.com/questions/67983163/…,developer.android.com/jetpack/androidx/releases/…。我的意思是片段,在活动中startResolutionForResult 仍在使用(developer.android.com/reference/android/app/…。onActivityResult 也已弃用。【参考方案7】:

我看到您对请求代码使用了不同的常量 REQUEST_CHECK_SETTINGSREQUEST_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 用户

此解决方案适用于ActivityFragment,只需对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()的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin iOS App 检查 GPS 是不是启用或提示用户

谷歌地图以编程方式启用 GPS?

明确识别是不是在后台启用了android服务位置和gps

ICS Android以编程方式启用gps?

Ionic 3 如何知道 GPS 是不是启用

检查是不是在android中启用了GPS