Android,
A Complete Course, From
Basics to Enterprise Edition
Android, A Complete Course, From Basics to Enterprise
Edition
Cet
article est extraie du livre « Android, A Complete Course »,
disponible sur Android2ee.com.
Les
exemples ou les programmes présents dans cet ouvrage sont fournis pour
illustrer les descriptions théoriques. Ce code est libre de toute utilisation
mais n'est pas distribuable.
La distribution
du code est reservée au site :
La
propriété intellectuelle du code appartient à :
L’utilisation
de ces codes est sous votre unique responsabilité et personne d’autre que vous
ne pourra être tenu responsable des préjudices ou dommages de quelques natures
que ce soit pouvant résulter de son utilisation.
Tous
les noms de produits ou marques cités dans cet ouvrage sont des marques déposés
par leurs propriétaires respectifs.
Publié par http://android2ee.com
Titre Original : Android, A Complete Course, From
Basics to Enterprise Edition. Édition Française.
ISBN : 979-10-90388-00-0
Copyright © 2011 by Mathias Séguy
Aucune
représentation ou reproduction, même partielle, autre que celles prévues à
l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut
être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant,
sans le respect des modalités prévues à l’article L. 122-10 dudit code
La géo localisation peut être effectuée soit :
· grâce au GPS (le plus courant),
· grâce à Galileo, dont la mise en œuvre est en cours (version européenne du GPS),
· grâce à la triangulation,
· grâce à un hot spot wifi, lui-même géo localisé.
Il y a trois objets à connaitre quand on fait de la géo localisation :
· Le LocationManager qui gère les LocationProvider, vous permet de les récupérer, de les lister, d’utiliser une liste de critères pour récupérer celui qui mappe le plus à ces derniers…
· Le LocationProvider qui vous fournit la Location, vous permet de connaître sa précision (Accuracy), son nom, s’il est payant, s’il nécessite un réseau téléphonique, un réseau internet, un réseau satellite, s’il fournit l’altitude, la boussole ou la vitesse.
· La Location qui donne la longitude, la latitude, l’altitude, la boussole (votre degré relativement au nord), la précision, la vitesse, l’heure de la dernière localisation… Toutes ces données ne sont pas toujours disponibles, on peut récupérer null ou vérifier avec has**.
Une chose à savoir, aussi, c’est que l’on n’obtient pas sa position en temps réel, mais que l’on demande la dernière position connue. La plupart du temps, votre application s’enregistre comme listener d’un changement de localisation.
Enfin, certains provider possède un attribut Bundle qui lui permet de stocker d’autres informations, à vous de les découvrir.
Enfin, la classe location possède des méthodes de calcul de distance.
Il faut demander les permissions pour utiliser ces services :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.paad.whereami">
<application android:icon="@drawable/icon">
<activity android:name=".MyGeoActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<!-- Permission to
access GPS permission-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
/>
<!-- Permission to
access WIFI or cellular network localization permission-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
/>
<!-- Permission to
access internet (usual)-->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
Et pour les tests, ACCESS_MOCK_LOCATION peut être utile comme permission.
Dans cet exemple, on utilise le location manager pour obtenir le location provider avec son nom. Puis on lui demande la dernière localisation connue et on l’affiche :
//Recupération
du location manager
LocationManager
locationManager;
String
context = Context.LOCATION_SERVICE;
locationManager
= (LocationManager)getSystemService(context);
//for using GPS
String
provider = LocationManager.GPS_PROVIDER;
//for WIFI or cellular network
//provider = LocationManager.NETWORK_PROVIDER;
Location
location =
locationManager.getLastKnownLocation(provider);
printLocation(location);
où :
private
String printLocation(Location location) {
String
strLocation;
if (location != null) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
float accuracy=location.getAccuracy();
double altitude=location.getAltitude();
float bearing=location.getBearing();
float speed=location.getSpeed();
long
time=location.getTime();
strLocation = "Votre
location est : \nLatitude: " + latitude +
"\nLongitude: "
+ longitude+
"\nAltitude: "
+ altitude+
"\nVotre angle (en
degrès) relativement au nord:" + bearing+
"\nVotre vitesse est:
" + speed+
"\nCes informations
sont valables avec une précision (en mètres) de :" +
accuracy+
"\nCes informations
date du " + new
Date(time).toString();
} else
{
strLocation = "Aucune
position trouvée";
}
return
strLocation;
}
Dans cet exemple nous utilisons des critères pour obtenir la localisation provider le plus proche de nos exigences :
//Recupération
du location manager
LocationManager
locationManager;
String
context = Context.LOCATION_SERVICE;
locationManager
= (LocationManager)getSystemService(context);
//Utilisation
de l’objet critères
Criteria
criteria = new
Criteria();
//Je souhaite une precision fine
criteria.setAccuracy(Criteria.ACCURACY_FINE);
//Je
n’utilise pas l’altitude
criteria.setAltitudeRequired(false);
//ni
la boussole
criteria.setBearingRequired(false);
//et
je me fiche que ce soit un service payant
criteria.setCostAllowed(true);
//Par
contre, je souhaite que ce soit peu gourmant en énergie
criteria.setPowerRequirement(Criteria.POWER_LOW);
//allez
renvoie moi, le LocationProvider qui répond le plus à mes critères
String
provider = locationManager.getBestProvider(criteria, true);
//Si
j’avais voulu le provider lui-même :
LocationProvider locationProvider=locationManager.getProvider(provider);
//mais
ce n’est pas la peine pour obtenir la dernière localisation
Location location =
locationManager.getLastKnownLocation(provider);
Dans cet exemple on montre
comment s’enregistrer en tant que listener d’un changement de
localisation :
//Recupération
du nom du LocationProvider
String
provider = locationManager.getBestProvider(criteria, true);
//je
m’enregistre en tant que listener d’un changement de localisation :
//
requestLocationUpdates (nomDuProvider,temps minimal avant d’être prévenu de
nouveau en milliseconde, //distance minimale de mouvement avant d’être prévenu
de nouveau, monlistener)
locationManager.requestLocationUpdates(provider,
1974, 10, locationListener);
//fin
de cette méthode
}
/**
* Mon listener de changement de localisation
*/
private final LocationListener locationListener = new LocationListener() {
//Est appelé dés que la localisation
change (attention cela prend en compte vos paramètres minTime et minDistance
quand il s’est enregistré.
public
void onLocationChanged(Location location) {
updateWithNewLocation(location);
}
//Est appelé quand le provider est arrété
public void onProviderDisabled(String provider){
updateWithNewLocation(null);
}
//Est appelé quand le provider est de
nouveau opérationnel
public void onProviderEnabled(String provider){ }
//Est appelé quand le
provider change de statu passage de enable a disable ou quand il n’arrive
pas
// a chercher sa localisation
public
void onStatusChanged(String provider, int
status,
Bundle extras){
}
};
//A
utiliser dans la méthode onDestroy (ou ailleurs pour dé inscruire votre
listener quand votre application a fini :
locationManager.removeUpdates(locationListener)
Android détecte la présence de l’appareil dans une zone que vous fixez. Cette zone est un cercle. Attention toute fois, n’oubliez pas qu’Android n’est pas en permanence en train de se localiser, alors ne mettez pas de rayon à votre cercle trop petit, sous peine de passer dans votre zone sans en être informé.
Pour utiliser ce système d’alerte, il faut utiliser les Intents pour récupérer l’évenement (cf le chapitre 5.3 Filtres d’intentions). Ensuite, voilà le code à utiliser. Le listener est le même que celui de l’exemple ci-dessus. Il sera prévenu quand vous êtes dans la zone, et tant que vous y restez.
//Usual
intent call with this
Intent intent = new Intent(this,
this.getClass());
PendingIntent pIntent = PendingIntent.getActivity(this,
0, intent, 0);
//Using the usual Intent
process with theIntent name
Intent intent2 = new
Intent("MyIntentCompleteName");
PendingIntent pIntent2 = PendingIntent.getBroadcast(this,
0, intent, 0);
//And then register:
(latitude, longitude, radius, expiration, intent)
locationManager.addProximityAlert(43.565715431592736d,
1.398482322692871d, 100, 1000000, pIntent);
où Latitude Longitude est le centre du cercle et radius son rayon. L’expiration est le temps pour lequel votre alerte est valable (je veux être alerté pendant n millisecondes (ou -1 pour que ce soit permanent).
N’oubliez pas de vous désenregistrer :
locationManager.removeProximityAlert(pIntent);
Le géo codage permet à partir d’une adresse d’obtenir la localisation associée, et vis-versa. Comme toujours, utiliser une thread ou un handler pour déporter le traitement dans une thread indépendante.
//First of all ask the permission in the manifest to go to internet
// Instancing the GeoCoder using this as Context (here this=my Activity)
and my Locale.
Geocoder
geoCoder = new
Geocoder(this,
Locale.FRANCE);
// Retrieve Adresses from location:
List<Address>
adresses = null;
Address
address;
double
latitude = 43.565715431592736d;
double
longitude = 1.398482322692871d;
int
maxResults = 10;
StringBuilder
strBuild=new
StringBuilder();
try {
adresses =
geoCoder.getFromLocation(latitude, longitude, maxResults);
address = adresses.get(0);
if(address!=null){
strBuild.append(address.getAddressLine(1));
strBuild.append(address.getPostalCode());
strBuild.append(address.getLocality());
strBuild.append(address.getCountryName());
//Do something with the string
}
}
catch
(IOException expe) {
// Do something
}
// Retrive Location from an Adress
String
myAdress="65 allée de bellefontaine, 31100,
Toulouse, France";
try {
adresses=geoCoder.getFromLocationName(myAdress,
maxResults);
address = adresses.get(0);
if(address!=null){
strBuild.append("The associated latitude is : ");
strBuild.append(address.getLatitude());
strBuild.append("and the longitude is : ");
strBuild.append(address.getLongitude());
//Do something with the string
}
}catch
(IOException expe) {
// Do something
}
Ce ne sont pas des composants obligatoires de la plate forme. Vous devez donc vérifier leur existence sur l’appareil.
De plus sachez qu’il vous faut vous enregistrer auprès de Google pour obtenir une clef qui vous permette d’utiliser GoogleMap (en fait deux clefs, l’un pour la production, l’autre pour le développement) : http://code.google.com/intl/fr/android/maps-api-signup.html et lisez bien la licence. Sinon vous pouvez vous tournez vers OpenStreetMap.
Les objets principaux pour pouvoir manipuler et afficher les cartes sont :
· MapActivity (qui s’apparente à ListActivity pour les activités voulant être des listes) et qui est la classe à étendre pour que votre Activity puisse afficher et gérer les cartes.
· MapView qui est l’objet graphique carte (comme TextView est l’objet Text).
· MapController qui vous permettra de gérer le comportement de votre MapView (zoomer, translater,…)
Ensuite les objets qui vous permettront d’afficher des informations supplémentaires sur votre carte (et qui doivent être considérés comme des calques que vous superposez les uns aux autres) sont :
· MyLocationOverlay qui est un calque prédéfini permettant d’afficher votre position courante (et votre direction)
· Overlay qui est la classe à étendre pour pouvoir définir vos propres calques (si vous ne voulez que rajouter des Items utilisez plutôt ItemzedOverlays)
· ItemizedOverlays qui est la classe à étendre si vous voulez ajouter des Items : des OverlaysItems et que vous ne souhaitez pas effectuer d’autre cutomisation.
Il y a trois éléments à modifier pour pouvoir le faire : le fichier manifest.xml, votre fichier de layout (main.xml) et enfin votre activité :
Il faut spécifier que vous souhaitez utiliser la librairie com.google.android.maps et que vous souhaitez accéder à internet.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.stinfoservices.net.android"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET"/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="com.google.android.maps"/>
<activity android:name=".StiMap" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="7" />
</manifest>
Il vous faut déclarer l’objet graphique map :
<com.google.android.maps.MapView
android:id="@+id/map_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="myapikey"
/>
Le chapitre 14.3 Retrouver sa clef pour vous permet de récupérer la clef de l’API à positionner dans le tag android:apiKey.
Faisons-nous plaisir avec la carte :
public class
StiMap extends MapActivity {
/**
The map View that displays the map */
MapView myMap;
/**
The overLay that displays your position */
MyLocationOverlay myPosOverlay;
/**
Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myMap = (MapView) findViewById(R.id.map_view);
/** Setting
my information displayed on the map */
//
Show satellite
myMap.setSatellite(true);
//
Show street View
myMap.setStreetView(false);
//
show traffic: You have to choose between streetView and Traffic
myMap.setTraffic(true);
/**
Display map controller to the user */
//
Zoom controller buttons (the boolean is TakeFocus)
myMap.displayZoomControls(true);
//
ou celui ci (the boolean is show zoom buttons control)
myMap.setBuiltInZoomControls(true);
//
The scroll
myMap.setScrollbarFadingEnabled(true);
myMap.setScrollContainer(true);
/**
* Getting information on the
displayed area (help you to focus your search using GeoCoding)
*/
//
The current zoom (1 for full earth and 21 for biggest available zoom)
int currentZoom = myMap.getZoomLevel();
//
The center point of the Map (the GeoPoint is the object used for localized
point)
//
location.lat*1E6=geoPoint.lat (the same for longitude)
GeoPoint center = myMap.getMapCenter();
//
The current latitude span (from the top edge to the bottom edge of the map) in
decimal
//
degrees multiplied by one million (1000000).
int latSpan = myMap.getLatitudeSpan();
//
The current longitude span (from the left edge to the right edge of the map) in
decimal
//
degrees multiplied by one million(1000000).
int lonSpan = myMap.getLongitudeSpan();
/**
Controlling the displayed area */
//
Getting the Map Controller
MapController mapController = myMap.getController();
//
Define where you want to go
Double realLatitude = 43.565715431592736d;
Double reallLongitude = 1.398482322692871d;
int geoLatAssociated = (int) (realLatitude * 1E6);
int geoLongAssociated = (int)
(reallLongitude * 1E6);
GeoPoint geoPoint = new
GeoPoint(geoLatAssociated, geoLongAssociated);
//
You can animate your map's movement
mapController.animateTo(geoPoint);
//
Or just go
mapController.setCenter(geoPoint);
//
You can set a zoomFactor (between 1 and 21)
int zoomLevel = 1;//
World vision
mapController.setZoom(zoomLevel);
//
or just Zoom in or out
mapController.zoomIn();
mapController.zoomOut();
//
And you can define the square you want to display
mapController.zoomToSpan(latSpan, lonSpan);
//
You can translate your view (but its in pixel)
int
x = 3, y = 4;
mapController.scrollBy(x, y);
/**
Managing the conversion between pixel and latitude/longitude */
Projection proj = myMap.getProjection();
//
Retrieve the GeoPoint associated to the pixel position (x,y)
geoPoint = proj.fromPixels(x,
y);
//
Converts the given GeoPoint to onscreen pixel coordinates,
//
relative to the top-left of the MapView that provided this Projection:
//
in - The latitude/longitude pair to convert.
//
out - A pre-existing object to use for the output; if null, a new Point will be
allocated
//
and returned.
GeoPoint geoPointToConvert = geoPoint;
Point point = new
Point();
proj.toPixels(geoPoint, point);
/**
Adding OverLays */
//
My own overlay
//
First retrieve the image you want to use to mark your item location on the map
Resources res = getResources();
Drawable drawable = res.getDrawable(R.drawable.myMarker);
MyItemizedOverlay myOverlay = new
MyItemizedOverlay(drawable);
myMap.getOverlays().add(myOverlay);
//
The standard MyLocationOverlay(Context,MapView)
//
here the context is this (==Current Acitivity)
myPosOverlay = new MyLocationOverlay(this, myMap);
myMap.getOverlays().add(myPosOverlay);
}
/* * (non-Javadoc) *
* @see
com.google.android.maps.MapActivity#onPause()
*/
@Override
protected void onPause() {
super.onPause();
//
As you added the MyLocationOverlay and shut it down in the onResume
//
you have to wake it again
myPosOverlay.enableMyLocation();
myPosOverlay.enableCompass();
}
/**
(non-Javadoc) *
* @see
com.google.android.maps.MapActivity#onResume()
*/
@Override
protected void onResume() {
super.onResume();
//
As you added the MyLocationOverlay you have to shut it down it
//
to optimize ressources, power and cpu
myPosOverlay.disableMyLocation();
myPosOverlay.disableCompass();
}
/* *
(non-Javadoc) *
* @see
com.google.android.maps.MapActivity#isRouteDisplayed() */
@Override
protected boolean isRouteDisplayed() {
//
Return true if you want your Activity to display direction
//
Return false else
return false;
}
}
Créer un overlay qui dérive d’ItemizedOverlay est assez trivial :
public class
MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
/** The item list to display*/
private List<OverlayItem> items = new
ArrayList<OverlayItem>();
/**
* @param
defaultMarker
*/
public MyItemizedOverlay(Drawable defaultMarker) {
super(defaultMarker);
Double latitude = 43.565715431592736d *
1E6;
Double longitude = 1.398482322692871d *
1E6;
GeoPoint geoPoint = new
GeoPoint(latitude.intValue(), longitude.intValue());
OverlayItem item = new
OverlayItem(geoPoint, "My Entreprise", "St Info
Services");
items.add(item);
//
... and so long for all the items you need to add
// but never
ever forget to call populate when you add or remove an item
populate();
} /*****************************************************************************************/
/** Method to implements ******************************************************************/ /*****************************************************************************************/
/* * (non-Javadoc) *
* @see
com.google.android.maps.ItemizedOverlay#createItem(int) */
@Override
protected OverlayItem createItem(int i) {
return items.get(i);
}
/* * (non-Javadoc) *
* @see
com.google.android.maps.ItemizedOverlay#size()
*/
@Override
public int size() {
return items.size();
} /*****************************************************************************************/
/** Methods to override for having a specific behavior
************************************/ /****************************************************************************************/
/* * (non-Javadoc) *
* @see
com.google.android.maps.ItemizedOverlay#onTap(int) */
@Override
protected boolean onTap(int index) {
//
This method is called when the user touch your item
//
So do what you want (a toast for exemple)
//
or do nothing:
return super.onTap(index);
}
}