Processing for Android

Location and permissions

Learn to access location using GPS, and properly set the sketch permissions in Android 6+ devices.

The Android location APIs

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.

Creating a location manager

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;

void setup () {
  size(480, 640);
  orientation(PORTRAIT);
}

void draw() {
}

void onPermissionsGranted() {  
  Context context = surface.getContext();
  locationListener = new MyLocationListener();
  locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);    
  locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
}

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 putting the manager and listener initialization code inside the onPermissionsGranted() function instead of setup() or the onResume() functions, 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. Because the app will show a dialog asking to allow or deny the permission, but wihtout pausing the app, you cannot rely on setup() or onResume() functions The onPermissionsGranted() will be called right after the user has granted all the dangerous permissions required by the app.

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:

Permission selection

Checking if location is available

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 (hasLocation) {
    text("Latitude: " + currentLatitude, 20, 40);
    text("Longitude: " + currentLongitude, 20, 75);
    text("Accuracy: " + currentAccuracy, 20, 110);
    text("Provider: " + currentProvider, 20, 145);
  } else {
    text("No permissions to access location", 20, 40);
  }
}

void onPermissionsGranted() {
  if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION) ||
      checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
    Context context = surface.getContext();    
    locationListener = new MyLocationListener();
    locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);    
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
    hasLocation = true;
  } else {
    hasLocation = false;
  }  
}

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:

Permission dialog

After allowing your app to access the location of the device, you will get the latitude and languitude values through the location listener!