Académique Documents
Professionnel Documents
Culture Documents
FK
Accueil > Cours > Créez des applications pour Android > TP : un explorateur de chiers
20 heures Moyenne
TP : un explorateur de chiers
Petit à petit, on se rapproche d'un contenu qui pourrait s'apparenter à celui des applications
professionnelles. Bien entendu, il nous reste du chemin à parcourir, mais on commence à vraiment
voir comment fonctionne Android !
A n de symboliser notre entrée dans les entrailles du système, on va s'affairer ici à déambuler dans
ses méandres. Notre objectif : créer un petit explorateur qui permettra de naviguer entre les chiers
contenus dans le terminal et faire en sorte de pouvoir exécuter certains de ces chiers.
Objectifs
Spéci cations techniques
Activité principale
La première chose à faire est de véri er qu'il est possible de lire la carte SD avec les méthodes vues
aux chapitres précédents. S'il est bien possible de lire la carte, alors on af che la liste des chiers du
répertoire, ce qui se fera dans une ListView . Cependant, comme notre mise en page sera
uniquement constituée d'une liste, nous allons procéder différemment par rapport à d'habitude. Au
lieu d'avoir une activité qui af che un layout qui contient une ListView , on va remplacer notre
Activity par une ListActivity . Comme l'indique le nom, une ListActivity est une activité
qui est principalement utilisée pour af cher une ListView Comme il s'agit d'une classe qui
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 1/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
qui est principalement utilisée pour af cher une ListView . Comme il s agit d une classe qui
dérive de Activity , il faut la traiter comme une activité normale, si ce n'est que vous n'avez pas
besoin de préciser un layout avec void setContentView (View view) , puisqu'on sait qu'il n'y a
qu'une liste dans la mise en page. Elle sera alors ajoutée automatiquement.
ListView getListView () . Cette ListView est une ListView tout à fait banale que vous
pouvez traiter comme celles vues dans le cours.
Adaptateur personnalisé
On associera les items à la liste à l'aide d'un adaptateur personnalisé. En effet, c'est la seule solution
pour avoir deux couleurs dans les éléments de la liste. On n'oubliera pas d'optimiser cet adaptateur
a n d'avoir une liste uide. Ensuite, on voudra que les éléments soient triés de la manière suivante :
Pour cela, on pourra utiliser la méthode void sort (Comparator<? super T> comparator) qui
permet de trier des éléments en fonction de règles qu'on lui passe en paramètres. Ces règles
implémentent l'interface Comparator de manière à pouvoir dé nir comment seront triés les
objets. Votre implémentation de cette interface devra redé nir la méthode
int compare(T lhs, T rhs) dont l'objectif est de dire qui est le plus grand entre lhs et rsh .
Si lhs est plus grand que rhs , on renvoie un entier supérieur à 0, si lhs est plus petit que
rhs , on renvoie un entier inférieur à 0, et s'ils sont égaux, on renvoie 0. Vous devrez véri er que
cette méthode respecte la logique suivante :
Si compare(a,b) > 0 et compare(b,c) > 0 , alors compare(a,c) > 0 quelque que soit la
combinaison ( a , b , c ).
Je comprends que ce soit un peu compliqué à comprendre, alors voici un exemple qui trie les
entiers :
java
1 import java.util.Comparator;
2
3 public class EntierComparator implements Comparator<Integer> {
4 @Override
5 public int compare(Integer lhs, Integer rhs) {
6 // Si lhs est supérieur à rsh, alors on retourne 1
7 if(lhs > rhs)
8 return 1;
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 2/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
;
9 // Si lhs est inférieur à rsh, alors on retourne -1
10 if(lhs < rhs)
11 return -1;
12 // Si lhs est égal à rsh, alors on retourne 0
13 return 0;
14 }
15 }
16
Préférences
Nous n'avons qu'une préférence ici, qui chez moi a pour identi ant repertoireColorPref et qui
contient la couleur dans laquelle nous souhaitons af cher les répertoires.
Comme il n'existe pas de vue qui permette de choisir une couleur, on va utiliser une vue développée
par Google dans ses échantillons et qui n'est pas incluse dans le code d'Android. Tout ce qu'il faut
faire, c'est créer un chier Java qui s'appelle ColorPickerView et d'y insérer le code suivant :
java
1 import android.content.Context;
2 import android.graphics.Canvas;
3 import android.graphics.Color;
4 import android.graphics.ColorMatrix;
5 import android.graphics.Paint;
6 import android.graphics.RectF;
7 import android.graphics.Shader;
8 import android.graphics.SweepGradient;
9 import android.view.MotionEvent;
10 import android.view.View;
11
12 public class ColorPickerView extends View {
13 public interface OnColorChangedListener {
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 3/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
14 void colorChanged(int color);
15 }
16
17 private Paint mPaint;
18 private Paint mCenterPaint;
Ce n'est pas grave si vous ne comprenez pas ce code compliqué, il permet juste d'af cher le joli
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 6/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
rond de couleur et de sélectionner une couleur. En fait, la vue contient un listener qui s'appelle
OnColorChangedListener . Ce listener se déclenche dès que l'utilisateur choisit une couleur. A n de
Notre préférence, elle, sera une boîte de dialogue qui af chera ce ColorPickerView . Comme il
s'agira d'une boîte de dialogue qui permettra de choisir une préférence, elle dérivera de
DialogPreference .
Notre préférence a un attribut de type int qui permet de retenir la couleur que choisit
l'utilisateur. Elle peut avoir un attribut de type OnColorChangedListener ou implémenter elle-
même OnColorChangedListener , dans tous les cas cette implémentation implique de redé nir la
fonction void colorChanged(int color) avec color la couleur qui a été choisie. Dès que
l'utilisateur choisit une couleur, on change notre attribut pour désigner cette nouvelle couleur.
On n'enregistrera la bonne couleur qu'à la fermeture de la boîte de dialogue, celle-ci étant marquée
par l'appel à la méthode void onDialogClosed(boolean positiveResult) avec positiveResult
qui vaut true si l'utilisateur a cliqué sur OK .
Options
Ouvrir le menu d'options ne permet d'accéder qu'à une option Cliquer sur celle ci enclenche un
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 7/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
Ouvrir le menu d options ne permet d accéder qu à une option. Cliquer sur celle-ci enclenche un
intent explicite qui ouvrira la PreferenceActivity .
Navigation
Il est recommandé de conserver un File qui représente le répertoire courant. On peut savoir si
un chier est un répertoire avec la méthode boolean isDirectory() et, s'il s'agit d'un répertoire,
on peut voir la liste des chiers qu'il contient avec File[] listFiles() .
Pour effectuer des retours en arrière, il faut détecter la pression du bouton adéquat. À chaque fois
qu'on presse un bouton, la méthode de callback
boolean onKeyDown(int keyCode, KeyEvent event) est lancée, avec keyCode un code qui
représente le bouton pressé et event l'évènement qui s'est produit. Le code du bouton
Retour arrière est KeyEvent.KEYCODE_BACK .
Soit on ne se trouve pas à la racine de la hiérarchie de chier, auquel cas on peut revenir en
arrière dans cette hiérarchie. Il faut passer au répertoire parent du répertoire actuel et ce
répertoire peut se récupérer avec la méthode File getParentFile() .
Soit on se trouve à la racine et il n'est pas possible de faire un retour en arrière. En ce cas, on
propose à l'utilisateur de quitter l'application avec la méthode de Context que vous
connaissez déjà, void finish() .
Visualiser un chier
Nous allons bien entendu utiliser des intents implicites qui auront pour action ACTION_VIEW . Le
problème est de savoir comment associer un type et une donnée à un intent, depuis un chier.
Pour la donnée, il existe une méthode statique de la classe Uri qui permet d'obtenir l'URI d'un
chier : Uri.fromFile(File file) . Pour le type, c'est plus délicat. Il faudra détecter l'extension du
chier pour associer un type qui corresponde. Par exemple, pour un chier .mp3 , on indiquera le
type MIME audio/mp3 . En n, si on veut moins s'embêter, on peut aussi passer le type MIME
audio/* pour chaque chier audio.
Pour rajouter une donnée et un type en même temps à un intent, on utilise la méthode
void setDataAndType(Uri data, String type) , car, si on utilise la méthode void setData(Uri) ,
alors le champ type de l'intent est supprimé, et si on utilise void setType(String) , alors le
champ data de l'intent est supprimé. Pour récupérer l'extension d'un chier, il suf t de récupérer
son nom avec String getName() , puis de récupérer une partie de ce nom : toute la partie qui se
trouve après le point qui représente l'extension :
java
1 fichier.getName().substring(fichier.getName().indexOf(".") + 1)
2
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 8/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
int indexOf(String str) va trouver l'endroit où se trouve la première instance de str dans la
chaîne de caractères, alors que String substring(int beginIndex) va extraire la sous-chaîne de
caractères qui se situe à partir de beginIndex jusqu'à la n de cette chaîne. Donc, si le chier
s'appelle chanson.mp3 , la position du point est 7 (puisqu'on commence à 0), on prend donc la
sous-chaîne à partir du caractère 8 jusqu'à la n, ce qui donne « mp3 ». C'est la même chose que si
on avait fait :
java
1 "musique.mp3".subSequence(8, "musique.mp3".length())
2
N'oubliez pas de gérer le cas où vous n'avez pas d'activité qui puisse intercepter votre intent.
Ma solution
Interface graphique
Facile, il n'y en a pas ! Comme notre activité est constituée uniquement d'une ListView , pas
besoin de lui attribuer une interface graphique avec setContentView .
Tout le raisonnement a déjà été expliqué dans les spéci cations techniques :
java
30 }
31
32 /**
33 * Déclenché à chaque fois que l'utilisateur choisit une couleur
34 */
35 public void colorChanged(int color) {
36 mColor = color;
37 }
38 }
39
Il faut ensuite ajouter cette boîte de dialogue dans le chier XML des préférences :
xml
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 package="sdz.chapitreTrois.explorateur"
3 android:versionCode="1"
4 android:versionName="1.0" >
5
6 <uses-sdk
7 android:minSdkVersion="7"
8 android:targetSdkVersion="7" />
9
10 <application
11 android:icon="@drawable/ic_launcher"
12 android:label="@string/app_name"
13 android:theme="@style/AppTheme" >
14 <activity
15 android:name=".ExplorateurActivity"
16 android:label="@string/title_activity_explorateur" >
17 <intent-filter>
18 <action android:name="android.intent.action.MAIN" />
19 <category android:name="android.intent.category.LAUNCHER" />
20 </intent-filter>
21 </activity>
22 <activity
23 android:name=".ExploreurPreference"
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 10/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
p
24 android:label="@string/title_activity_exploreur_preference" >
25 </activity>
26 </application>
27 </manifest>
28
L'activité principale
Attributs
1 /**
2 * Représente le texte qui s'affiche quand la liste est vide
3 */
4 private TextView mEmpty = null;
5
6 /**
7 * La liste qui contient nos fichiers et répertoires
8 */
9 private ListView mList = null;
10
11 /**
12 * Notre adaptateur personnalisé qui lie les fichiers à la liste
13 */
14 private FileAdapter mAdapter = null;
15
16 /**
17 * Représente le répertoire actuel
18 */
19 private File mCurrentFile = null;
20
21 /**
22 * Couleur voulue pour les répertoires
23 */
24 private int mColor = 0;
25
26 /**
27 * Indique si l'utilisateur est à la racine ou pas
28 * Pour savoir s'il veut quitter
29 */
30 private boolean mCountdown = false;
31
32 /**
33 * Les préférences partagées de cette application
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 11/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
34 */
35 private SharedPreferences mPrefs = null;
36
1 /**
2 * Se déclenche dès qu'une préférence a changé
3 */
4 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
5 mColor = sharedPreferences.getInt("repertoireColorPref", Color.BLACK);
6 mAdapter.notifyDataSetInvalidated();
7 }
8
L'adaptateur
J'utilise un Adapter que j'ai créé moi-même a n d'avoir des items de la liste de différentes
couleurs :
java
1 /**
2 * L'adaptateur spécifique à nos fichiers
3 */
4
5 private class FileAdapter extends ArrayAdapter<File> {
6 /**
7 * Permet de comparer deux fichiers
8 *
9 */
10 private class FileComparator implements Comparator<File> {
11 public int compare(File lhs, File rhs) {
12 // Si lhs est un répertoire et pas l'autre, il est plus petit
13 if(lhs.isDirectory() && rhs.isFile())
14 return -1;
15 // Dans le cas inverse, il est plus grand
16 if(lhs.isFile() && rhs.isDirectory())
17 return 1;
18
19 // Enfin, on ordonne en fonction de l'ordre alphabétique sans tenir compte de la casse
20 return lhs.getName().compareToIgnoreCase(rhs.getName());
21 }
22 }
23
24 public FileAdapter(Context context, int textViewResourceId, List<File> objects) {
25 super(context, textViewResourceId, objects);
26 mInflater = LayoutInflater.from(context);
27 }
28
29 private LayoutInflater mInflater = null;
30
31 /**
32 * Construit la vue en fonction de l'item
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 12/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
33 */
34 public View getView(int position, View convertView, ViewGroup parent) {
35 TextView vue = null;
36
37 if(convertView != null)
38 // On recycle
39 vue = (TextView) convertView;
40 else
41 // On inflate
42 vue = (TextView) mInflater.inflate(android.R.layout.simple_list_item_1, null);
43
44 File item = getItem(position);
45 //Si c'est un répertoire, on choisit la couleur dans les préférences
46 if(item.isDirectory())
47 vue.setTextColor(mColor);
48 else
49 // Sinon, c'est du noir
50 vue.setTextColor(Color.BLACK);
51
52 vue.setText(item.getName());
53 return vue;
54 }
55
56 /**
57 * Pour trier rapidement les éléments de l'adaptateur
58 */
59 public void sort () {
60 super.sort(new FileComparator());
61 }
62 }
63
Méthodes secondaires
1 /**
2 * On enlève tous les éléments de la liste
3 */
4
5 public void setEmpty() {
6 // Si l'adaptateur n'est pas vide…
7 if(!mAdapter.isEmpty())
8 // Alors on le vide !
9 mAdapter.clear();
10 }
11
J'ai aussi développé une méthode qui me permet de passer d'un répertoire à l'autre :
java
1 /**
2 * Utilisé pour naviguer entre les répertoires
3 * @param pFile le nouveau répertoire dans lequel aller
4 */
5
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 13/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
11 mCountdown = false;
12
13 // On change le répertoire actuel
14 mCurrentFile = pFile;
15 // On vide les répertoires actuels
16 setEmpty();
17
18 // On récupère la liste des fichiers du nouveau répertoire
19 File[] fichiers = mCurrentFile.listFiles();
20
21 // Si le répertoire n'est pas vide…
22 if(fichiers != null) {
23 // On les ajoute à l'adaptateur
24 for(File f : fichiers)
25 mAdapter.add(f);
26 // Puis on le trie
27 mAdapter.sort();
28 }
29 }
30
1 /**
2 * Utilisé pour visualiser un fichier
3 * @param pFile le fichier à visualiser
4 */
5 private void seeItem(File pFile) {
6 // On crée un intent
7 Intent i = new Intent(Intent.ACTION_VIEW);
8
9 String ext = pFile.getName().substring(pFile.getName().indexOf(".") + 1).toLowerCase();
10 if(ext.equals("mp3"))
11 i.setDataAndType(Uri.fromFile(pFile), "audio/mp3");
12 /** Faites en autant que vous le désirez */
13
14 try {
15 startActivity(i);
16 // Et s'il n'y a pas d'activité qui puisse gérer ce type de fichier
17 } catch (ActivityNotFoundException e) {
18 Toast.makeText(this, "Oups, vous n'avez pas d'application qui puisse lancer ce fichier",
Toast.LENGTH_SHORT).show();
19 e.printStackTrace();
20 }
21 }
22
Les menus
Rien d'étonnant ici, normalement vous connaissez déjà tout. À noter que j'ai utilisé deux layouts
pour le menu contextuel de manière à pouvoir le changer selon qu'il s'agit d'un répertoire ou d'un
chier :
java
1 @Override
2 public boolean onCreateOptionsMenu(Menu menu) {
3 getMenuInflater().inflate(R.menu.activity_explorateur, menu);
4 return true;
5 }
6
7 @Override
8 public boolean onOptionsItemSelected (MenuItem item)
9 {
10 switch(item.getItemId())
11 {
12 case R.id.menu_options:
13 // Intent explicite
14 Intent i = new Intent(this, ExploreurPreference.class);
15 startActivity(i);
16 return true;
17 }
18 return super.onOptionsItemSelected(item);
19 }
20
21 @Override
22 public void onCreateContextMenu(ContextMenu menu, View vue, ContextMenuInfo menuInfo) {
23 super.onCreateContextMenu(menu, vue, menuInfo);
24
25 MenuInflater inflater = getMenuInflater();
26 // On récupère des informations sur l'item par apport à l'adaptateur
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 15/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
26 // On récupère des informations sur l item par apport à l adaptateur
27 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
28
29 // On récupère le fichier concerné par le menu contextuel
30 File fichier = mAdapter.getItem(info.position);
31 // On a deux menus, s'il s'agit d'un répertoire ou d'un fichier
32 if(!fichier.isDirectory())
33 inflater.inflate(R.menu.context_file, menu);
34 else
35 inflater.inflate(R.menu.context_dir, menu);
36 }
37
38 @Override
39 public boolean onContextItemSelected(MenuItem item) {
40 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)
item.getMenuInfo();
41 // On récupère la position de l'item concerné
42 File fichier = mAdapter.getItem(info.position);
43 switch (item.getItemId()) {
44 case R.id.deleteItem:
45 mAdapter.remove(fichier);
46 fichier.delete();
47 return true;
48
49 case R.id.seeItem:
50 seeItem(fichier);
51 return true;
52 }
53 return super.onContextItemSelected(item);
54 }
55
onCreate
22
23 // On récupère la racine de la carte SD pour qu'elle soit le répertoire consulté au départ
24 mCurrentFile = Environment.getExternalStorageDirectory();
25
26 // On change le titre de l'activité pour y mettre le chemin actuel
27 setTitle(mCurrentFile.getAbsolutePath());
28
29 // On récupère la liste des fichiers dans le répertoire actuel
30 File[] fichiers = mCurrentFile.listFiles();
31
32 // On transforme le tableau en une structure de données de taille variable
33 ArrayList<File> liste = new ArrayList<File>();
34 for(File f : fichiers)
35 liste.add(f);
36
37 mAdapter = new FileAdapter(this, android.R.layout.simple_list_item_1, liste);
38 // On ajoute l'adaptateur à la liste
39 mList.setAdapter(mAdapter);
40 // On trie la liste
41 mAdapter.sort();
42
43 // On ajoute un Listener sur les items de la liste
44 mList.setOnItemClickListener(new OnItemClickListener() {
45
46 // Que se passe-t-il en cas de clic sur un élément de la liste ?
47 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
48 File fichier = mAdapter.getItem(position);
49 // Si le fichier est un répertoire…
50 if(fichier.isDirectory())
51 // On change de répertoire courant
52 updateDirectory(fichier);
53 else
54 // Sinon, on lance l'item
55 seeItem(fichier);
56 }
57 });
58 }
59 }
60
Télécharger le projet
Améliorations envisageables
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 17/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
android:id="@android:id/list" . Si vous voulez q'un widget ou un layout s'af che quand la liste
est vide, vous devez lui attribuer l'identi ant android:id="@android:id/empty" . Pour ma
correction, j'ai le XML suivant :
xml
Petite digression pour vous dire que le design pattern singleton a pour objectif de faire en
sorte que vous ne puissiez avoir qu'une seule instance d'un objet. C'est pourquoi on utilise la
méthode getSingleton() qui renvoie toujours le même objet. Il est impossible de
construire autrement un objet de type MimeTypeMap .
propose de ne gérer que le cas où le périphérique externe est enlevé, auquel cas l'action est
ACTION_MEDIA_REMOVED . Notez au passage que l'action pour dire que la carte fonctionne à nouveau
est ACTION_MEDIA_MOUNTED .
Comme nous l'avons vu dans le cours, il faudra déclarer notre broadcast receiver dans le
Manifest :
xml
1 <receiver android:name=".ExplorerReceiver"
2 android:exported="false">
3 <intent-filter>
4 <action android:name="android.intent.action.MEDIA_REMOVED" />
5 <action android:name="android.intent.action.MEDIA_MOUNTED" />
6 </intent-filter>
7 </receiver>
8
Ensuite, dans le receiver en lui-même, on fait en sorte de viser la liste des éléments s'il y a un
problème avec le périphérique externe, ou au contraire de la repeupler dès que le périphérique
fonctionne correctement à nouveau. À noter que dans le cas d'un broadcast Intent avec l'action
Le professeur
Frédéric Espiau
Livre PDF
OPENCLASSROOMS
ENTREPRISES
CONTACT
EN PLUS
Français
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 20/21
20/06/2020 TP : un explorateur de fichiers - Créez des applications pour Android - OpenClassrooms
https://openclassrooms.com/fr/courses/2023346-creez-des-applications-pour-android/2027006-tp-un-explorateur-de-fichiers 21/21