Ajout d’incident

J’ai passé 2h30 à compléter mon application Android pour http://incidents-transports.com (qui a changé de nom aujourd’hui)

J’ai ajouté une nouvelle activité pour rapporter un nouvel incident.

Il me fallait donc un référentiel des lignes existantes. Je n’ai pas fait compliqué, et ai codé ce référentiel en dur. Grosso-modo c’et une Hashmap qui fait:

"Metro" : {"1", "2", "3", "3bis", etc.},
"RER": {"A", "B", "C", "D", "E"}

Puis j’ai défini l’interface utilisateur. Un peu comme Benoît sur iPhone, j’ai mis un premier Spinner qui permet de choisir le type de ligne (« Métro », « RER », etc.) et un second qui permet de choisir la ligne exacte.

[code]
Spinner spinnerLineName = (Spinner) findViewById(R.id.SpinnerLineName);

public void onItemSelected(AdapterView< ?> parentAdapter,
View selectedItemView, int position, long id) {
lineType= parentAdapter.getItemAtPosition(position).toString();
String[] linenames = TransportationProvider.getLines(lineType);
ArrayAdapter adapter = new ArrayAdapter(
parentAdapter.getContext(), R.layout.spinnerlinename, linenames);
spinnerLineName.setAdapter(adapter);
}
[/code]

Et pour finir, j’ai ajouté une méthode post(Incident incident) sur mon IncidentProvider.

Dans la journée, Olivier Girardot, m’a donné l’API pour poster. Il m’a fait peur parce que l’ajout d’un incident ne peut se faire qu’en JSON. Mais miracle, Android gère très simplement le JSON.

[code]
public static void post(Incident newIncident) throws JSONException {
JSONObject json=new JSONObject();
json.put("line_name",newIncident.ligne);
json.put("reason", newIncident.reason);
json.put("source", "rds");//TODO
String req=json.toString();

URI uri= new URI(DEFAULT_ADD_SERVICE_URL);

HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(uri);
StringEntity body;
try {
body = new StringEntity(req);
httpPost.setEntity(body);
HttpResponse response = httpClient.execute(httpPost);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
[/code]

Les incidents du réseau RATP sur votre mobile!

Vous avez sans doute entendu parler des menaces de la RATP contre le site http://incidents-ratp.com/ qui a pour but de

Vous donner l’accès librement et gratuitement aux informations concernant vos transports en communs

Quoi qu’il en soit, Benoît est en train de développer une application iPhone pour accéder plus rapidement aux informations trafic des transports en commun.

Et il m’a suggéré de faire la même chose pour Android. J’ai plein de trucs importants à faire, mais une envie soudaine de coder m’a pris.

Hier, j’ai commencé. Et il me paraît intéressant pour tous les développeurs en herbe de dire combien un tel développement consomme de temps. Hier, j’ai donc passé 4 heures pour traiter les tâches suivantes.

Découvrir le service web

J’ai d’abord regardé l’API du service. C’est encore un peu sommaire.

La liste des incidents est obtenu par un appel REST, dans le format de son choix (XML ou json).

Je me dis que Java permettra de parser sans problème le XML.

(15 min)

Préparer mon environnement

Ensuite, je perds un peu de temps à réinitialiser mon Eclipse avec le dernier plugin de développement Android.

Pas vraiment utile, vu que je vais me baser sur une ancienne version du SDK, afin de couvrir le maximum de matériels.

(15 min)

Initier un nouveau projet

Ça s’est l’affaire d’un ou deux clics :-)
J’utilise bitbucket pour outiller ma gestion de configuration.

Je laisse l’interface vide pour le moment.

(10 min)

Faire le client de service web

Je crée ensuite un modèle objet pour un Incident. Il est très simple. Oui, j’utilise des variables de classe publiques. Je n’ai jamais compris la convention des getters&setters. Alors, pour un développement mobile, j’évite les lourdeurs. (30 min)

[code]
public class Incident {
int uid;

String status;
String ligne;

int votePlus;
int voteEnded;
int voteMinus;

String reason;

Date lastModified;
}
[/code]

Il me faut évidemment un client du service web. Cela se fait très simplement avec Apache HttpClient qui fait partie du framework Android. (20 min).

[code]
public class IncidentProvider {
private static final String TAG = "IncidentProvider";
public void refresh(URI uri) throws ClientProtocolException, IOException,
SAXException {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(uri);
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
refresh(entity.getContent());
}
}
[/code]

Je passe ensuite au mappage du résultat dans des objets Incident. Je pensais au début faire du castor/Jaxb/etc. mais c’est trucs sont trop lourds pour un mobile. De base dans le framework, on a du SAX:
[code]
public class IncidentProvider implements ContentHandler {
private Incident mIncident;
ArrayList@lt;incident@gt; incidents;

/* buffers */
private StringBuffer mBuffer;
[...]
public void refresh(InputStream in) throws IOException, SAXException {
Xml.parse(in, Encoding.UTF_8, this);
}

public void startDocument() throws SAXException {
mIncidents = new ArrayList@lt;Incident@gt;();
}

public void startElement(String uri, String name, String qName,
Attributes atts) throws SAXException {
if ("resource".equals(name)) {
mIncident = new Incident();
}
mBuffer = new StringBuffer();
}

public void endElement(String uri, String name, String qName)
throws SAXException {
if ("status".equals(name)) {
mIncident.status = mBuffer.toString();
} else if ("vote_plus".equals(name)) {
mIncident.setVotePlus(mBuffer.toString());
} else if ("vote_minus".equals(name)) {
mIncident.setVoteMinus(mBuffer.toString());
} else if ("vote_ended".equals(name)) {
mIncident.setVoteEnded(mBuffer.toString());
} else if ("reason".equals(name)) {
mIncident.reason = mBuffer.toString();
} else if ("line".equals(name)) {
mIncident.ligne = mBuffer.toString();
} else if ("last_modified_time".equals(name)) {
mIncident.setLastModified(mBuffer.toString());
} else if ("uid".equals(name)) {
mIncident.setUID(mBuffer.toString());
} else if ("resource".equals(name)) {
mIncidents.add(mIncident);
}
}

public void characters(char chars[], int start, int length)
throws SAXException {
mBuffer.append(chars, start, length);
}
[/code]
(1h avec les tests unitaires et l’écriture des setters dont j’ai besoin)

Affichage dans une liste

Je modifie ensuite l’interface pour ajouter une ListView qui présentera les résultats.

C’est là dessus que j’ai passé le plus de temps (2h). J’ai d’abord cru que je devrais écrire mon propre Adapter, alors que j’ai finalement pu utiliser le ArrayAdapter. Ensuite, j’ai mal compris comment celui-ci utilisait le TexteView.

Et c’est le « bonheur » du développement XML. Il faut exécuter dans l’émulateur (plusieurs secondes à chaque fois) pour avoir un message d’erreur pas toujours très clair… ou bien une appli qui fonctionne mais n’affiche rien.

In fine

Mais j’ai fini par faire marcher cette partie.
Copie d'écran

Et ce soir, j’avais mieux à faire ;-)