[JAVA] I tried to make an activity that collectively sets location information

background

There are two main things to do when using location on Android.

  1. Enable Permission
  2. Turn on the location information setting of the terminal

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.

Thing you want to do

Roughly speaking, create an Activity that processes with the following flow.

  1. Call Activity
  2. Check Permission first --Not permitted: Request permission (dialog display) --Already allowed: → 3
  3. Next, check the location information setting --Not set: Make a setting request (dialog display) --Already set: Return the result to the caller

The actual deliverable is here

The actual code is described below.

1. Call Activity

(Please check the sample of the deliverable here)

2. Check Permission first

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.

permission_dialog.png

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.

3. Next, check the location information setting

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.

less than v11.0

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)

locationsetting.png

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);
        }
    }
}

v11.0 or higher

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);
            }
        }
    });
}

Finally

That's why the result is here.

Recommended Posts

I tried to make an activity that collectively sets location information
I tried to make an OCR application with PySimpleGUI
I tried to get the location information of Odakyu Bus
[Python] I tried to make an application that calculates salary according to working hours with tkinter
I tried to make a system that fetches only deleted tweets
I tried to make an analysis base of 5 patterns in 3 years
[Python] Simple Japanese ⇒ I tried to make an English translation tool
I tried to make an image similarity function with Python + OpenCV
I tried to make a site that makes it easy to see the update information of Azure
I want to make an automation program!
I tried to make a Web API
I tried to make an original language "PPAP Script" that imaged PPAP (Pen Pineapple Appo Pen) with Python
I tried to make an open / close sensor (Twitter cooperation) with TWE-Lite-2525A
I tried to make a skill that Alexa will return as cold
I tried to get an image by scraping
I tried to make AI for Smash Bros.
I tried to detect an object with M2Det!
I tried to make a ○ ✕ game using TensorFlow
[Python] I tried to make a Shiritori AI that enhances vocabulary through battles
I tried to make a translation BOT that works on Discord using googletrans
I tried to make a dictionary function that does not distinguish between cases
I tried my best to make an optimization function, but it didn't work.
I tried to make a "fucking big literary converter"
I tried to make an air lip detection & automatic response BOT for remote work
[LPIC 101] I tried to summarize the command options that are easy to make a mistake
I tried to make an automatic character dialogue generator by N floor Markov chain
Continuation ・ I tried to make Slackbot after studying Python3
I tried to implement an artificial perceptron with python
I tried to debug.
I tried to get an AMI using AWS Lambda
I tried to become an Ann Man using OpenCV
I tried to make a memo app that can be pomodoro, but a reflection record
I tried to visualize the spacha information of VTuber
I tried to paste
It's Cat Day, so I tried to make something that translates into cat-like words.
I tried to find an alternating series with tensorflow
[1 hour challenge] I tried to make a fortune-telling site that is too suitable with Python
I tried to make a generator that generates a C # container class from CSV with Python
I tried to make Othello AI that I learned 7.2 million hands by deep learning with Chainer
I tried to make a bot that randomly acquires Wikipedia articles and tweets once a day
I tried to make various "dummy data" with Python faker
I tried to notify the train delay information with LINE Notify
I tried to get Web information using "Requests" and "lxml"
I tried to make a stopwatch using tkinter in python
I tried to make GUI tic-tac-toe with Python and Tkinter
I tried to get various information from the codeforces API
I tried to make a simple text editor using PyQt
I tried to create an article in Wiki.js with SQLAlchemy
I tried to learn PredNet
I tried to organize SVM.
I tried to implement PCANet
I tried to reintroduce Linux
I tried to introduce Pylint
I tried to summarize SparseMatrix
I tried to touch jupyter
I tried to implement StarGAN (1)
I tried to build an estimation model of article titles that are likely to buzz with Qiita
I tried to make it possible to automatically send an email just by double-clicking the [Python] icon
I tried to make an image classification BOT by combining TensorFlow Lite and LINE Messaging API
I tried HR Tech to develop an expert search engine by machine learning in-house meeting information
[Python] I tried to make a simple program that works on the command line using argparse.