Fournisseurs de contenus
- Accès à l'API : LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE)
-
Localisation apportée par un LocationProvider plus ou moins précis, consommateur d'énergie, coûteux
- Providers par défaut : GPS_PROVIDER, NETWORK_PROVIDER, PASSIVE_PROVIDER (provider "lazy")
- Tous les providers : List<String> getAllProviders()
- Meilleur provider selon une liste de critères : ``String getBestProvider(Criteria criteria, boolean enabled)
- Un provider identifié par son nom : LocationProvider getProvider(String name)
D'autres providers (non disponibles actuellement mais envisageables) :
- Localisation par utilisation du capteur photo (reconnaissance de lieux)
- Localisation par signature accélérométrique (permet notamment de déterminer une ligne de métro employée) ou par centrale à inertie
- Localisation par signature sonore (reconnaissance de proximité de balises émettant des ultrasons inaudibles pour l'homme)
- ...
Obtenir des localisations
-
Dernière localisation connue : LocationManager.getLastKnownLocation(String provider) (peut retourner null si le provider est indisponible)
- Retourne un objet Location avec méthodes getLatitude(), getLongitude() (en degrés), getAltitude() (en mètres), getTime() (en ms depuis l'epoch)
-
LocationManager.requestLocationUpdates(String provider, long minTimeMillis, float minDistance, LocationListener listener) pour obtenir des MAJ de localisation
- minTimeMillis et minDistance définissent des périodes temporelles et spatiales de MAJ
- LocationListener doit implanter une méthode onLocationChanged(Location) appelée à chaque localisation fournie
- Désenregistrement du listener avec removeUpdates(LocationListener)
-
void addProximityAlert(double lat, double lon, float radius, long expirationInMillis, PendingIntent intent) pour ajouter une alerte de proximité
- L'intent est envoyé dès que l'on passe dans la zone spécifiée ; l'alerte expire après expirationInMillis (pas d'expiration si == -1)
- Suppression de l'alerte avec removeProximityAlert(PendingIntent intent)
Gestion des permissions
-
Permissions nécessaires :
- android.permission.ACCESS_COARSE_LOCATION : pour une localisation basée sur les bornes cellulaires et Wi-Fi
- android.permission.ACCESS_FINE_LOCATION : pour une localisation utilisant le récepteur GPS
-
android.permission.ACCESS_BACKGROUND_LOCATION : pour autoriser la localisation également en arrière-plan (depuis un service) ; permission à utiliser avec parcimonie car permettant théoriquement le suivi permanent de l'utilisateur
- Permission non-obligatoire pour un service en avant-plan (lire ceci)
- Permissions dangereuses nécessitant un accord préalable de l'utilisateur (voir ici)
Un service loggant les localisations
/** A simple service that logs locations into a file.
* It must be activated by a companion activity that must check that the location permissions are enabled.
*
* @author chilowi at u-pem.fr
*/
public class LocationLoggerService extends Service
{
public static final String LOCATION_FILE = "locations.log";
public static final String LOCATION_BROADCAST_ACTION = LocationLoggerService.class.getName() + ".LOCATION_INFO";
LocationManager locationManager = null;
LocationListener locationListener = null;
Writer writer = null;
private static boolean running = false;
public static boolean isRunning()
{
return running;
}
@Override
public IBinder onBind(Intent arg0) { return null; }
public static final String CHANNEL_ID = "locationLogger";
/** This method creates a new notification channel (required for API 26+)
* It is copied from https://developer.android.com/training/notify-user/build-notification
*/
private void createNotificationChannel()
{
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Location logger channel";
String description = "Channel for the location logger";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
private Notification createNotification()
{
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_media_play)
.setContentTitle("Location logger")
.setContentText("The logger is active")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSilent(true);
return mBuilder.build();
}
private void log(String message)
{
try {
if (writer == null)
{
writer = new OutputStreamWriter(openFileOutput(LOCATION_FILE, MODE_APPEND));
}
writer.write(new Date() + ": " + message);
writer.flush();
} catch (IOException e)
{
Log.e(getClass().getName(), "Cannot log message " + message + " due to an exception", e);
}
}
@Override
public void onCreate()
{
createNotificationChannel();
locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
locationListener = new LocationListener() {
@Override public void onStatusChanged(String provider, int status, Bundle extras)
{
log(String.format("Change of status of provider %s: %d", provider, status));
}
@Override public void onProviderEnabled(String provider)
{
log(String.format("Provider %s is enabled", provider));
}
@Override public void onProviderDisabled(String provider)
{
log(String.format("Provider %s is disabled", provider));
}
@Override public void onLocationChanged(Location location)
{
log(String.format("latitude=%f, longitude=%f, altitude=%f",
location.getLatitude(), location.getLongitude(), location.getAltitude()));
Intent i = new Intent(LOCATION_BROADCAST_ACTION);
i.putExtra("latitude", location.getLatitude());
i.putExtra("longitude", location.getLongitude());
sendBroadcast(i);
}
};
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startForeground(1, createNotification());
try {
String provider = intent.getStringExtra("provider");
if (provider == null) provider = LocationManager.GPS_PROVIDER;
locationManager.requestLocationUpdates(provider,
intent.getLongExtra("minTime", 10000),
intent.getFloatExtra("minDistance", 100.0f),
locationListener);
running = true;
} catch (SecurityException e)
{
Toast.makeText(this, "Cannot start the location logger service", Toast.LENGTH_LONG).show();
stopSelf();
}
return Service.START_REDELIVER_INTENT; // Restart the service with the intent if it is destroyed
}
@Override
public void onDestroy()
{
try { if (writer != null) writer.close(); } catch (IOException ignored) {}
try {
locationManager.removeUpdates(locationListener);
} catch (SecurityException e)
{
// this case should not be encountered
}
running = false;
}
}