There are two main things to do when using location on Android.
There are several ways to enable the above two, but now there is a mechanism that allows you to operate the process only inside the app.
However, the method is a little troublesome ... or rather, it is a little troublesome for the following reasons even though I want to set it all together. .. ..
--The calling activity is interrupted when the permission dialog to be set is used. --Activity required to receive callback (Activity method required) --Google Api Client is required if it is less than v11.0 of google-play-services
So, this time, I created a "dedicated activity that enables both Permission and location information settings together" so that it can be used from anywhere.
Roughly speaking, create an Activity that processes with the following flow.
The actual deliverable is here
The actual code is described below.
(Please check the sample of the deliverable here)
First, let's talk a little about Permission.
With the introduction of the Runtime Permission mechanism from OS 6.0 or higher, there are the following two Permission (dangerous designation) that require permission while the application is running as location information.
(For details on Permission itself, see Official Reference etc.)
permission | Contents |
---|---|
ACCESS_COARSE_LOCATION | Network-based access to approximate location |
ACCESS_FINE_LOCATION | Access to specific location information such as GPS |
Therefore, first of all, in onCreate of Activity, check whether the above Permission is permitted (in the case of OS 6.0 or higher), and process as follows.
--Not allowed: Request permission --Already allowed: Move to location information setting process
private static final int REQUEST_CODE_LOCATION_PERMISSION = 1;
private static final String[] PERMISSIONS = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isPermissionGranted()) {
// (6.Less than 0 or already allowed)
//Since it is permitted, set the location information of the terminal
requestLocationSetting();
return;
}
// (6.0 or more)
//Request permissions
ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_CODE_LOCATION_PERMISSION);
}
private boolean isPermissionGranted() {
for (String permission : PERMISSIONS) {
if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
When a Permission request is made, the following dialog will be displayed.
Next, we will receive a callback from the dialog, but if the permission is successful, we will move to the location information setting, and if we refuse, we will end it.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (permissions.length != grantResults.length) {
setResultCode(Locset.ResultCode.PERMISSION_FAILURE);
return;
}
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
//Rejected
// 1.Returns false if you pass permissions that you have never requested
// 2.If the requested permission is rejected with a check mark "Do not show again", false is returned.
// 3.Returns true if the requested permissions have been denied and are not checked to hide again
//Note) Note that 1 and 2 cannot be distinguished.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
//Return result to caller (failure)
finishForResult(Locset.ResultCode.PERMISSION_FAILURE);
} else {
//Return result to caller (failure)
finishForResult(Locset.ResultCode.PERMISSION_FAILURE_DO_NOT_ASK_AGAIN);
}
return;
}
}
//Since it is permitted, set the location information of the terminal
requestLocationSetting();
}
private void finishForResult(@Locset.ResultCode int resultCode) {
setResult(resultCode);
finish();
}
This is the end of Permission processing.
You can set the location information directly from the device setting screen or notification area, but by using the function of Google Play Services, you can set it directly from within the app. Can be set.
For now, add play-services-location to build.gradle.
implementation 'com.google.android.gms:play-services-location:11.8.0'
By the way, the implementation is slightly different depending on the version of Google Play Services. To be clear, v11 and above are much easier to implement.
version | Implementation method |
---|---|
11.Less than 0 | Requires Google Api Client LocationServices.Use Settings Api reference |
11.0 or more | Use Settings Client reference |
In addition, the location information setting can take into consideration the accuracy of the desired information and battery consumption. Basically, you can specify the following four Priority.
public final class LocationRequest extends zzbfm implements ReflectedParcelable {
public static final int PRIORITY_HIGH_ACCURACY = 100;
public static final int PRIORITY_BALANCED_POWER_ACCURACY = 102;
public static final int PRIORITY_LOW_POWER = 104;
public static final int PRIORITY_NO_POWER = 105;
On Android, the location information setting items are slightly different depending on the OS version (may be by model), but using this Google Play Services has the advantage of making the settings suitable for the above Priority.
If it is less than v11, you need to connect Google Api Client first.
private void requestLocationSetting() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
finishForResult(Locset.ResultCode.GOOGLE_API_FAILURE);
}
})
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
//Start getting location information settings after connecting
requestLocationSettingGoogleApi();
}
@Override
public void onConnectionSuspended(int i) {
finishForResult(Locset.ResultCode.GOOGLE_API_FAILURE);
}
}).build();
mGoogleApiClient.connect();
}
After connecting to GoogleApiClient, we will actually check the location information setting of the terminal and perform the process to display the dialog for setting. (As a supplement, this setting process is possible without Permission)
private static final int REQUEST_CODE_LOCATION_SETTING = 2;
private void requestLocationSettingGoogleApi() {
final LocationSettingsRequest request = new LocationSettingsRequest.Builder()
.setAlwaysShow(true) //If you do not do this, you will not be able to call next if you do not change it
.addLocationRequest(LocationRequest.create().setPriority(mPriority))
.build();
final PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(
mGoogleApiClient,
request);
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
@Override
public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
//The location information of the specified priority can be used
finishForResult(Locset.ResultCode.SUCCESS);
return;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
//Display a dialog to ask the user to change the location information settings
status.startResolutionForResult(LocsetActivity.this, REQUEST_CODE_LOCATION_SETTING);
return;
} catch (IntentSender.SendIntentException e) {
// failure
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
//Called when location information cannot be obtained and it is difficult to recover from that state
break;
}
finishForResult(Locset.ResultCode.LOCATION_SETTING_FAILURE);
}
});
}
Finally, onActivityResult receives the callback of the setting result.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//Receive a callback from the location information setting dialog
if (requestCode == REQUEST_CODE_LOCATION_SETTING) {
if (resultCode == Activity.RESULT_OK) {
finishForResult(Locset.ResultCode.SUCCESS);
} else {
finishForResult(Locset.ResultCode.LOCATION_SETTING_FAILURE);
}
}
}
In v11 and above, GoogleApiClient is no longer required and is done using a class called SettingsClient. There is no particular change in how you receive the callback.
private void requestLocationSetting() {
final LocationSettingsRequest request = new LocationSettingsRequest.Builder()
.setAlwaysShow(true)
.addLocationRequest(LocationRequest.create().setPriority(mPriority))
.build();
final Task<LocationSettingsResponse> result = LocationServices.getSettingsClient(this)
.checkLocationSettings(request);
result.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() {
@Override
public void onComplete(@NonNull Task<LocationSettingsResponse> task) {
try {
task.getResult(ApiException.class);
//Already set
finishForResult(Locset.ResultCode.SUCCESS);
} catch (ApiException exception) {
switch (exception.getStatusCode()) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
//Display a dialog to ask the user to change the location information settings
resolvable.startResolutionForResult(LocsetActivity.this, REQUEST_CODE_LOCATION_SETTING);
return;
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
} catch (ClassCastException e) {
// Ignore, should be an impossible error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
//Called when location information cannot be obtained and it is difficult to recover from that state
break;
}
finishForResult(Locset.ResultCode.LOCATION_SETTING_FAILURE);
}
}
});
}
That's why the result is here.