Learn to access location using GPS, and properly set the sketch permissions in Android 6+ devices.
An Android device can determine its location with various degrees of accuracy by using information from the network is connected to (WiFi, cellular), or from the signals from the GPS (Global Positioning System) satellites. GPS-based location is more accurate, but requires to be outdoors and consumes more battery and updates less frequently than network-based location.
The basic structure of a sketch that access location information is very similar to what we saw for other sensors. You get the location manager from the context of the app, wallpaper, or watch face using the surface.getContext()
call in version 4.0+ of the mode (in 3 you can just get a reference to the activity with getActivity()
), and then attach a listener to the manager:
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.Manifest;
LocationManager locationManager;
MyLocationListener locationListener;
boolean hasLocation = false;
void setup () {
fullScreen();
orientation(PORTRAIT);
textFont(createFont("SansSerif", 26 * displayDensity));
textAlign(CENTER, CENTER);
requestPermission("android.permission.ACCESS_FINE_LOCATION", "initLocation");
}
void draw() {
}
void initLocation(boolean granted) {
if (granted) {
Context context = getContext();
locationListener = new MyLocationListener();
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
hasLocation = true;
} else {
hasLocation = false;
}
}
class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
println(location.getLatitude(), location.getLongitude());
}
public void onProviderDisabled (String provider) { }
public void onProviderEnabled (String provider) { }
public void onStatusChanged (String provider, int status, Bundle extras) {
}
}
However, here we are explicitly requesting the permission with the requestPermission()
function, and are putting the manager and listener initialization code inside the initLocation() function instead of the setup()
function, as in previous examples. The reason is that in recent versions of Android (6.0 and newer), permissions are divided into two protection levels: normal and dangerous. The permissions classified as dangerous affect access to private data of the user, such as location or list of contacts, and require the user to grant them individually when he or she runs the app, not when it is installed.
The app will show a dialog asking to allow or deny the permission, and only then the initLocation() function will be called with the result of the request in the boolean argument. This function, which should contain the initialization code corresponding to the permission being requested, does not need to be called initLocation, it can have a different name, but the two important rules that one needs to follow are that this function must receive a boolean argument, and that its name must be provided in requestPermission()
.
However, you still need to check the permissions needed by the sketch, in this case ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION for network and GPS-based location, using the permissions selector in the PDE:
The Location variable we receive in the location listener contains all the information needed to specify the location in latitude and longitude:
class MyLocationListener implements LocationListener {
public void onLocationChanged(Location location) {
currentLatitude = (float)location.getLatitude();
currentLongitude = (float)location.getLongitude();
currentAccuracy = (float)location.getAccuracy();
currentProvider = location.getProvider();
}
public void onProviderDisabled (String provider) {
currentProvider = "";
}
public void onProviderEnabled (String provider) {
currentProvider = provider;
}
public void onStatusChanged (String provider, int status, Bundle extras) {
}
}
Even if we add the location permissions to the sketch through the selector, the user can still deny them when running the app on the device, so we need to handle the situation where the location is not available. We also have the checkPermission()
to make sure that a given permission has been actually granted and we can access the associated functionality:
void draw() {
background(0);
if (hasPermission("android.permission.ACCESS_FINE_LOCATION")) {
text("Latitude: " + currentLatitude + "\n" +
"Longitude: " + currentLongitude + "\n" +
"Accuracy: " + currentAccuracy + "\n" +
"Provider: " + currentProvider, 0, 0, width, height);
} else {
text("No permissions to access location", 0, 0, width, height);
}
}
The complete code of this example is available here. Once you run it on a device with Android 6 or higher, you should see the following dialog requesting the location permissions:
After allowing your app to access the location of the device, you will get the latitude and languitude values through the location listener!