Tutoriel Android : Apprendre à utiliser les ressources

Chapitre 4.1

Android2ee

Mathias Seguy

Ce chapitre vous présente une notion essentielle de la programmation Android, la notion de ressource. Vous rencontrerez les images, les chaînes de caractères, les couleurs, les styles et les thèmes et quelques bonnes pratiques associées.

Ce tutoriel présente l'utilisation des ressources avec Android. Il fait partie du livre "Android a complete course" dont les chapitres sont listés ci-dessous :

- Chapitre 0 : Installation et préparation de l'environnement de développement

- Chapitre 1 : Premiers pas avec Android

- Chapitre 1.1 : Cycle de vie de l'activité

- Chapitre 2.1 : Les éléments graphiques fondamentaux

- Chapitre 2.2 : Composants graphiques complexes

- Chapitre 3.3 : Intent et IntentFilters

- Chapitre 4.1 : Les Ressources

- Chapitre 4.2 : SharedPreferences et PreferenceActivity

- Chapitre 4.3 : Base de données SQLite

- Chapitre 4.4 : Gestion des fichiers

- Chapitre 5 : Content Provider

Pour réagir à ce tutoriel, un espace de dialogue vous est proposé sur le forum : Commentez

- Titre : Tutoriel Android : Apprendre à utiliser les ressources

- Auteur : Mathias Seguy

- Parution : 8 avril 2016

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2016 Mathias Seguy. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Définition

- II. Fichiers de ressources

- II-A. Gestion des chaînes de caractères : l'internationalisation

- II-A-1. Le cas des chaînes de caractères HTML

- II-B. Les images

- II-B-1. Exemple d'utilisation des ressources

- II-B-2. Pour aller au-delà

- II-C. Les fichiers XML

- II-D. Les couleurs

- II-E. Les styles et thèmes

- II-F. La localisation des ressources

- III. Remerciements

I - Définition

Nous pouvons distinguer plusieurs types de ressources :

- les fichiers de ressources de l'application (images, chaînes de caractères, layout, xml) qui dépendent du contexte (langue française, taille de l'écran, mode portrait/paysage…) ;

- les bases de données ;

- les fichiers préférences ;

- les fichiers pour la lecture et la lecture/écriture sans contexte.

Toutes ces ressources se trouvent dans votre application sous res/.

II - Fichiers de ressources

Ces types de ressources sont celles utilisées par l'application et interprétées par le système Android. Elles sont de quatre types :

- res\drawable pour les images ;

- res\raw pour les ressources brutes, les fichiers sans contexte dont nous parlerons au chapitre suivant ;

- res\values pour les chaînes de caractères, les couleurs, les tableaux et les dimensions ;

- res\xml pour les fichiers XML.

II-A - Gestion des chaînes de caractères : l'internationalisation

Pour la gestion de l'internationalisation, la convention veut que le dossier contienne la langue associée : res\values-fr pour le français, res\values-en pour l'anglais et ainsi de suite. « en » et « fr » sont, respectivement, les valeurs usuelles des langues anglaise et française lors de l'internationalisation. Sa gestion devient alors transparente au développeur.

Il y a cinq types de chaînes, les normales, celles avec paramètres, celles au singulier/pluriel, celles en tableaux  et celles au format HTML dont nous ne parlerons pas.

Ainsi le fichier suivant présente les quatre types de chaînes qui nous intéressent :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--Simple string-->
<string name="hello">Hello World, ResourcesTuto!</string>
<string name="app_name">ResourcesTuto</string>
<string name="simpleString">Good Morning My name is Bond, James Bond</string>

<!--String with parameters ($s for string, $d for decimal (any decimal: integer, double, float...)-->
<string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>

<!--String array-->
<string-array name="i_like_array">
<item>chocolate</item>
<item>television</item>
<item>internet</item>
<item>nicotine</item>
<item>hug</item>
<item>Santa Claus</item>
</string-array>

<!--Plurals String-->
<plurals name="singleOrPlural">
<item quantity="one">I am alone on moon</item>
<item quantity="other">I am 6 900 000 000 on earth</item>
</plurals>

</resources>

Les chaînes simples s'utilisent, soit dans le code :

getString(R.string.simpleString)

soit dans le layout.xml pour déclarer un composant en utilisant son identifiant :

<TextView
android:id="@+id/simpleStringTV"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>

Les chaînes avec paramètres s'utilisent avec un formateur.

Dans le fichier strings.xml, nous avons déclaré la chaîne :

<!--String with parameters ($s for string, $d for decimal (any decimal:integer, double, float...)-->
<string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>

Nous la manipulons ainsi en Java :

// Then instanciate a string with paremeter
String parameterString=String.format(getString(R.string.param_message),"peace","war",2);
// And set the text view
TextView textViewWithParam=(TextView)findViewById(R.id.parameterStringTV);
textViewWithParam.setText(parameterString);

Les chaînes peuvent être regroupées sous forme de tableaux :

<!--String array-->
<string-array name="i_like_array">
<item>chocolate</item>
<item>television</item>
<item>internet</item>
<item>nicotine</item>
<item>hug</item>
<item>Santa Claus</item>
</string-array>

Dans le code, celui-ci se manipule ainsi :

// Instanciate the resources object:
Resources resources=getResources();
String[] planets = resources.getStringArray(R.array.i_like_array);

Les chaînes peuvent aussi posséder un genre singulier ou pluriel :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="numberOfSongsAvailable">
<item quantity="one">One song found.</item>
<item quantity="other">%d songs found.</item>
</plurals>
</resources>

et s'utilisent dans le code ainsi :

int count = getNumberOfHuman();
Resources res = getResources();
String humans = res.getQuantityString(R.plurals.singleOrPlural, count);

Il faut faire attention aux signes ‘ et "

<string name="good_example">"This'll work"</string>
<string name="good_example_2">This\'ll also work</string>
<string name="bad_example">This doesn't work</string>

II-A-1 - Le cas des chaînes de caractères HTML

Celui-ci est un cas particulier, car le code HTML est du texte à balises, ce qui perturbe le parser XML et empêche la compilation de votre code.

Une manière de contourner le problème est de mettre la balise &lt en lieu et place du <.

Un exemple, dans votre fichier string.xml :

<string name="html_head">&lt;html>&lt;body>&lt;table>&lt;tr>&lt;th width=\"50%\">Jour&lt;/th>&lt;th>Basse&lt;/th>&lt;th>Haute&lt;/th>&lt;th>Tendance&lt;/th>&lt;/tr></string>
<string name="html_loop">&lt;tr>&lt;td align=\"center\">%1$s&lt;/td>&lt;td align=\"center\">%2$s&lt;/td>&lt;/td>&lt;td align=\"center\">%3$s&lt;/td>&lt;td>&lt;img src=\"%4$s\">&lt;/td>&lt;/tr></string>
<string name="html_foot">&lt;/table>&lt;/body>&lt;/html></string>

Le code Java pour utiliser cette chaîne :

StringBuffer bufResult = new StringBuffer(getResources().getString(R.string.html_head));
String tempLine;
for (Forecast forecast : forecasts) {
tempLine=String.format(getResources().getString(R.string.html_loop),
forecast.getDay(),forecast.getLowTemp(),forecast.getHighTemp(),
forecast.getIcon());
bufResult.append(tempLine);
}
bufResult.append(getResources().getString(R.string.html_foot));
browser.loadDataWithBaseURL(null, bufResult.toString(), "text/html", "UTF-8", null);

Noter l'absence de l'appel aux méthodes fromHtml et toHtml qui ne nous ont pas paru efficientes.

II-B - Les images

Les formats acceptés sont PNG et JPEG (GIF n'est pas recommandé).

Il suffit de déposer les fichiers image dans res\drawable pour qu'ils soient référencés par le système.

Ainsi l'image res/drawable/myimage.png peut être utilisée,

soit dans le code :

Resources res = getResources();
Drawable drawable = res.getDrawable(R.drawable.myimage);

soit dans la description des IHM dans le fichier de layout :

<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/myimage" />

II-B-1 - Exemple d'utilisation des ressources

Cet exemple possède quatre fichiers, le code, le fichier des chaînes de caractères, le fichier de layout et enfin un fichier selector qui n'est là que pour le plaisir et qui choisit une image en fonction de l'état de l'objet sur lequel il est.

Cet exemple montre des TextView, une scroll et deux ImageButton instanciés, soit directement dans leur déclaration XML, soit dans le code Java.

Le fichier des strings (res\values\strings.xml) :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--Simple string-->
<string name="hello">Hello World, ResourcesTuto!</string>
<string name="app_name">ResourcesTuto</string>
<string name="simpleString">Good Morning My name is Bond, James Bond</string>
<!--String with parameters ($s for string, $d for decimal (any decimal:integer, double, float...)-->
<string name="param_message">Hello, i love %1$s, i hate %2$s, i am %3$d years old</string>
<string name="spinner">Do you like: </string>
<!--String array-->
<string-array name="i_like_array">
<item>chocolate</item>
<item>television</item>
<item>internet</item>
<item>nicotine</item>
<item>hug</item>
<item>Santa Claus</item>
</string-array>
<!--Plurals String-->
<plurals name="singleOrPlural">
<item quantity="one">I am alone on moon</item>
<item quantity="other">I am 6 900 000 000 on earth</item>
</plurals>
<string name="button">An image on the button</string>
</resources>

Le fichier de déclaration des layouts :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/simpleStringTV"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello">
</TextView>
<TextView
android:id="@+id/simpleStringToSetTV"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</TextView>
<TextView
android:id="@+id/parameterStringTV"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</TextView>
<TextView
android:id="@+id/pluralsTV"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</TextView>
<Spinner
android:id="@+id/Spinner01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:prompt="@string/spinner">
</Spinner>
<ImageButton
android:id="@+id/ImageButtonXmlInstanciatedIB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon">
</ImageButton>
<ImageButton
android:id="@+id/ImageButtonToInstanciateIB"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageButton>
</LinearLayout>

Le fichier Java :

public class ResourcesTuto extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Instanciate the resources object:
Resources resources=getResources();

// Then instanciate the textView @+id/simpleStringToSetTV
TextView textViewSimple=(TextView)findViewById(R.id.simpleStringToSetTV);
textViewSimple.setText(getString(R.string.simpleString));

// Then instanciate a string with paremeter
String parameterString=String.format(getString(R.string.param_message),"peace","war",2);
// And set the text view
TextView textViewWithParam=(TextView)findViewById(R.id.parameterStringTV);
textViewWithParam.setText(parameterString);

// Then instanciate a string with plurals or not
int number=0;
if (Math.random()<0.5d) {
number=1;
} else {
number=2;
}
String pluralsString=resources.getQuantityString(R.plurals.singleOrPlural, number);
// And its associated textView
TextView textViewPlurals=(TextView)findViewById(R.id.pluralsTV);
textViewPlurals.setText(pluralsString);
String[] planets = resources.getStringArray(R.array.i_like_array);

// Then instanciate the spinner using the array string
Spinner spinner = (Spinner) findViewById(R.id.Spinner01);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.i_like_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

// Then find a ressource for the ImageButton
Drawable drawableBck=resources.getDrawable(R.drawable.selector);
Drawable drawable=resources.getDrawable(R.drawable.icon);
// And set it:
ImageButton imageButton=(ImageButton)findViewById(R.id.ImageButtonToInstanciateIB);
imageButton.setBackgroundDrawable(drawableBck);
imageButton.setImageDrawable(drawable);
}
}

Et enfin le fichier de sélection d'une image (res\drawable\selector.xml) :

<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- when focused -->
<item android:state_focused="true" android:drawable="@drawable/sky"/>
<!-- when pressed -->
<item android:state_pressed="true" android:drawable="@drawable/coffee" />
<!-- when normal -->
<item android:drawable="@drawable/icon" />
</selector>

Vous remarquerez qu'en fonction de l'état, le fichier pointe sur une ressource différente. Ces ressources sont des fichiers png stockés sous res\drawable et nommés coffee.png et sky.png. Il est à remarquer que les états, pour un ImageButton quand il a le focus, passent de focused à normal puis à pressed. Les valeurs possibles pour un tel fichier sont :

<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize=["true" | "false"]
android:dither=["true" | "false"]
android:variablePadding=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
android:state_window_focused=["true" | "false"]>
</item>
</selector>

II-B-2 - Pour aller au-delà

http://developer.android.com/guide/topics/resources/drawable-resource.html

En fait il existe neuf types d'images différentes pour Android :

- Bitmap File sont les fichiers .png, .jpeg et .gif présentés ci-dessus ;

- Nine-Patch file est un fichier png avec des régions étirables pour permettre le redimensionnement de l'image ;

- Layer List est un tableau d'images ;

- StateList est un fichier XML qui référence  des images pour des états différents (typiquement pour un bouton qui possède plusieurs états focus, pressed, released). Nous venons de le voir au sein du fichier selector.xml ;

- LevelList est un fichier XML qui référence  des images pour des niveaux différents, chaque image est associée à une valeur numérique maximale ;

- TransitionDrawable est un fichier XML qui référence  deux images réalisant une morphose de l'une à l'autre ;

- ClipDrawable est un fichier XML qui décrit une image se dévoilant petit à petit (via du code) ;

- ScaleDrawable  est un fichier XML décrivant une image qui pourra être zoomée ;

- ShapeDrawable est un fichier XML qui décrit une forme géométrique (ovale, rectangle…). C'est une image dessinée de manière informatique comme un graphique 2D.

Enfin, pour aller encore plus loin, il faut aller voir 2D Graphics :

http://developer.android.com/guide/topics/graphics/2d-graphics.html

II-C - Les fichiers XML

Ils se trouvent sous res\xml\ et on y accède par getResources().getXml(R.xml.nomDuFichier) qui retourne un XmlPullParser sur le fichier.

try {
XmlPullParser xpp = getResources().getXml(R.xml.words);

while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
if (xpp.getEventType() == XmlPullParser.START_TAG) {
if (xpp.getName().equals("word")) {
items.add(xpp.getAttributeValue(0));
}
}
xpp.next();
}
} catch (Throwable t) {
Toast.makeText(this, "Request failed: " + t.toString(), 4000).show();
}

Le XmlPullParser est assez simpliste comme parseur, il ne connaît que les méthodes :

- next qui renvoie la ligne suivante ;

- getName qui renvoie le nom de la balise ;

- getValue qui renvoie la valeur contenue au sein de la balise ;

- et getEventType qui renvoie :

- End_Document quand le document est fini,

- Start_Tag quand il rencontre une balise ouvrante,

- End_Tag quand il en rencontre une fermante.

Autant dire que pour la manipulation de fichier XML, il vaut mieux rester humble.

II-D - Les couleurs

Elles se placent sous res\values et peuvent se nommer color.xml :

<resources>
<color name="opaque_red">#f00</color>
<color name="translucent_red">#80ff0000</color>
</resources>

Chaque couleur est décrite par son nom et son code RGB hexadécimal.

Dans le code, elles s'utilisent comme ça :

Resources res = getResources();
int color = res.getColor(R.color.opaque_red);

et dans un fichier de ressources :

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@color/translucent_red"
android:text="Hello"/>

II-E - Les styles et thèmes

La convention souhaite que ce fichier soit enregistré sous res\values\styles.xml, il peut alors être référencé dans votre application par la balise @style/MonStyle.

Les styles et les thèmes d'une application peuvent être surchargés ou définis :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="ToDoTheme" parent="@android:style/Theme.Black">
<item name="android:textSize">12sp</item>
</style>
</resources>

ou bien :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyTheme">
<item name="android:textSize">12sp</item>
<item name="android:textColor">#111</item>
</style>
<style name="MySubTheme" parent="MyTheme"; >
<item name="android:textSize">8sp</item>
</style>

</resources>

Et il sera utilisé dans le descripteur de ressources (le layout.xml) de la manière suivante :

<TextView
style="@style/CodeFont"
android:text="@string/hello" />

De plus, un style peut être appliqué à un ensemble de composants ayant spécifié un inputType, ou plus exactement la notion d'inputType peut être incluse dans le style. Ainsi, au lieu d'écrire :

<EditText
android:inputType="number"
... />

on écrira un style spécifique pour les éléments graphiques de type inputType=number :

<style name="Numbers">
<item name="android:inputType">number</item>
...
</style>

que l'on associera à cet élément (et à tous ceux qui sont de type inputType number) :

<EditText
style="@style/Numbers"
... />

Lorsque l'on affecte à l'ensemble de l'activité ou de l'application un style, cela s'appelle un thème. Ce thème se spécifie dans le fichier manifest.xml (et non plus dans le layout.xml).

Pour l'application :

<application android:theme="@style/CustomTheme">

Pour une activité de l'application :

<activity android:theme="@android:style/Theme.Dialog">

Cela marche de manière identique aux styles, on peut les spécifier et déclarer les nôtres (on les appelle par @style/MonTheme)

<style name="CustomDialogTheme" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@drawable/custom_dialog_background</item>
</style>

et on l'applique :

<activity android:theme="@style/CustomDialogTheme">

La liste des styles Android se trouve ici : http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/styles.xml;h=d7b654e49809cb97a35682754b1394af5c8bc88b;hb=HEAD

et celles des thèmes sous : http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/themes.xml;h=6b3d7407d1c895a3c297e60d5beac98e2d34c271;hb=HEAD

Pour utiliser ces thèmes et ces styles afin de les surcharger, il suffit de remplacer l'underscore par un point :« @android:style/Theme.NoTitleBar ».

II-F - La localisation des ressources

Ou, comment Android décide quel est le fichier de ressources à utiliser. Cela se comprend bien dans le cas de l'internationalisation, mais ce concept est étendu à l'arbre des devices Android. Cette liste de paramètres pris en compte par Android est la suivante :

- code réseau et code mobile du pays (le pays et le réseau de la carte SIM) dit code MCC (Mobile Country Code) et MNC (Mobile Network Code);http://en.wikipedia.org/wiki/Mobile_Network_Code;

- langue et région (internationalisation usuelle) ;

- taille de l'écran (small, medium et large) ;

- le rapport largeur/longueur (long ou not long) de l'écran ;

- l'orientation de l'écran (port pour portrait, land pour landscape ou square pour carré) ;

- densité de l'écran en pixel (ldpi pour low dpi, mdpi pour medium dpi et hdpi pour high dpi) ;

- type d'écran tactile (notouch pour non sensitif, stylus pour le stylet et finger pour sensitif) ;

- visibilité du clavier (keyexposed pour clavier physique apparent, keyshidden pour clavier physique caché ou keysoft pour un clavier logiciel) ;

- type de clavier (nokeys, qwerty ou 12Key) ;

- type de navigation (nonav pour pas de navigation, dpad pour le pad, trackball pour trackball et wheel pour la roue de la souris) ;

Ainsi, pour définir la branche dans laquelle se trouve votre ressource, il faut spécifier son chemin :

- il faut définir dans l'ordre des paramètres ci-dessus listés ;

- on peut omettre des éléments ;

- il faut un unique qualificateur par type de paramètre ;

- il faut les séparer par un trait d'union.

Ainsi mcc234-mcn20-en-rUK-small-long-port-ldpi-finger-keysoft-qwerty-dpad spécifie le chemin pour le Royaume-Uni, pour l'opérateur « Hutchison 3G UK Ltd », en langue anglaise pour le Royaume-Uni, pour un petit portable au format 16/9, en mode portrait, avec peu de densité de points par inch, qui est sensitif au toucher, avec un clavier logiciel de type qwerty dont la navigation se fait au pad.

Maintenant que nous avons bien conscience du délire complet d'établir une application Android pour l'arbre complet des choix, il faut comprendre la stratégie d'Android pour choisir quelles ressources il va utiliser et comment minimiser le nombre de ressources spécifiques que nous allons implémenter.

Android choisira le dossier contenant le nombre de qualificateurs qui correspondent le plus avec ceux du portable utilisateur, et dans la négative, il choisira celui dont la locale est identique. Cela permet une épuration drastique de cet arbre : n'utiliser que ce qui vous paraît vraiment pertinent. Ensuite si Android ne trouve pas de correspondance, il enverra une exception, donc il faut toujours définir, pour toutes les ressources , le dossier par défaut, celui qui ne contient pas de qualificateur. Donc toujours avoir un dossier res\values\, res\drawable, de même pour vos ressources layout, anim, raw, xml et tutti quanti.

Lorsqu'Android détecte un changement sur le matériel de l'une de ces valeurs, il détruit et reconstruit l'application. Pour tempérer ce comportement, il faut rajouter dans la description de votre activité la ligne android :configChanges= « orientation|keyboardhidden ». Cela supprime ce comportement pour les paramètres spécifiés et déclenche la méthode onConfigurationChanged de votre activité. Les valeurs possibles sont orientation, keyboardhidden, fontscale, locale, keyboard, touchscreen et navigation. Dans le code, il suffit d'évaluer _newConfig.orientation dans la méthode OnConfigChanged pour retrouver la nouvelle orientation et associer le traitement qui s'ensuit, de même pour les autres paramètres.

Les consignes d'Android concernant ce sujet sont :

- concevez votre application pour qu'elle marche pour toutes les locales (res\values doit toujours exister) ;

- concevez avec des layouts flexibles qui s'adaptent à la taille de l'écran, pas de taille en dur ;

- ne créez pas plus de ressources que nécessaire ;

- utilisez le contexte Android pour avoir un « look-up » manuel : context.getResources().getConfiguration().locale.getDisplayName();

- testez vos applications avec différentes configurations ;

- définissez la liste des paramètres que vous prenez en compte, gravez la dans le marbre, testez toutes ces configurations et publiez cette liste quand vous déposez votre application sur l'Android market

III - Remerciements

Nous tenons à remercier FRANOUCH pour la relecture orthographique de cet article et Mickael Baron pour la mise au format Developpez.com.