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