I was perusing StackOverflow for Android items and came across this question Getting the Contents of a Specific Directory on the SDCard. I love StackOverflow… but for some reason I can’t post xml code examples and the code field doesn’t always work currently so you end up with code outside of the code box…. So, anyway, I decided to make an example for Edward and post it here so that everything would be formatted correctly.

This examples focus is the FileUtils class that I created for the purpose of finding files recursively on the SD Card. Here is the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class FileUtils {

    public File[] listFilesAsArray(File directory, FilenameFilter[] filter,
            int recurse) {
        Collection<File> files = listFiles(directory, filter, recurse);

        File[] arr = new File[files.size()];
        return files.toArray(arr);
    }

    public Collection<File> listFiles(File directory,
            FilenameFilter[] filter, int recurse) {

        Vector<File> files = new Vector<File>();

        File[] entries = directory.listFiles();

        if (entries != null) {
            for (File entry : entries) {
                for (FilenameFilter filefilter : filter) {
                    if (filter == null
                            || filefilter
                                    .accept(directory, entry.getName())) {
                        files.add(entry);
                        Log.v("FileUtils", "Added: "
                                + entry.getName());
                    }
                }
                if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) {
                    recurse--;
                    files.addAll(listFiles(entry, filter, recurse));
                    recurse++;
                }
            }
        }
        return files;
    }
}

Edward needs to find files by type (specifically ringtones) from a path… either on the phone (as in the following example) or on the SD Card. He could use the MediaStore.Audio.Media Content Provider to accomplish this task… however, it would give him all of the audio media on the phone. It is sometimes necessary to list files by a particular mimetype without using the Content Providers. In my example, this is accomplished by using a list of file extensions and searching the desired path for files that match those file extensions. There are two ways you could go about it… you could make a String[] array of the extensions or you could use the Resources array as I have done. Below is the xml for the mimetypes we are looking for.

/res/values/mimetypes.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="audio">
<item>aif</item>
<item>aifc</item>
<item>aiff</item>
<item>au</item>
<item>mid</item>
<item>mp3</item>
<item>ogg</item>
<item>snd</item>
<item>wav</item>
</string-array>
</resources>

Using a Resources string array is very convenient and provides a way for you to store arrays in files so that you can use them on demand rather than keeping them in memory. They are very simple to use. In my example you can see how they are used in the FindFiles function as seen here:

1
2
3
4
Resources resources = getResources();
// array of valid audio file extensions
String[] audioTypes = resources.getStringArray(R.array.audio);
FilenameFilter[] filter = new FilenameFilter[audioTypes.length];

Of particular interest in the above example is the FilenameFilter object. This allows us to define an array of file extension filters that works very quickly and only returns the files we want.

Here is the example application that I created for Edward. It lists all of the audio files in /system/media/audio/ringtones/ and displays them in a ListView. When you click a particular file in the ListView it starts playing. There is a menu item to stop the ringtone from playing (there seems to be an issue with MediaPlayer.setLooping(false) when dealing with ogg files) and to quit the application. The complete Eclipse project is available for download at the bottom of the post.

/res/layout/main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:drawSelectorOnTop="false" />
    <TextView android:id="@android:id/empty"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="No ringtones found." />
</LinearLayout>

/res/layout/list_item.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/ringtone"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="40dip"
    android:textSize="18dip"
    android:textStyle="bold"
    android:textColor="#FFFFE0" />


/src/com/androidworks/findfiles/FindFilesByType.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package com.androidworkz.findfiles;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;

public class FindFilesByType extends ListActivity {

    private static final int EXIT = 0;
    private static final int STOP = 1;
    private static final String DIRECTORY = "/system/media/audio/ringtones/";
    private MediaPlayer mp = new MediaPlayer();
    List<String> Ringtones = new ArrayList<String>();
    Boolean hasErrors = false;
    int currentPosition = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ListView lv = getListView();
        File ringtones_directory = new File(DIRECTORY);
        if (!ringtones_directory.exists()) {
            AlertDialog.Builder ad = new AlertDialog.Builder(
                    FindFilesByType.this);
            ad.setTitle("Directory Not Found");
            ad.setMessage("Sorry! The ringtones directory doesn't exist.");
            ad.setPositiveButton("OK", null);
            ad.show();
            hasErrors = true;
        }
       
        if (!ringtones_directory.canRead()) {
            AlertDialog.Builder ad = new AlertDialog.Builder(
                    FindFilesByType.this);
            ad.setTitle("Permissions");
            ad.setMessage("Sorry! You don't have permission to list the files in that folder");
            ad.setPositiveButton("OK", null);
            ad.show();
            hasErrors = true;
        }
        else {
            Ringtones = FindFiles(false);

            if (Ringtones.size() < 1) {
                AlertDialog.Builder ad = new AlertDialog.Builder(
                        FindFilesByType.this);
                ad.setTitle("Permissions");
                ad.setMessage("Sorry! No ringtones exists in " + DIRECTORY + ".");
                ad.setPositiveButton("OK", null);
                ad.show();
                Log.e("Ringtones", "No ringtones were found.");
                hasErrors = true;
            }
        }      
       
        if (!hasErrors) {
            setListAdapter(new ArrayAdapter<String>(FindFilesByType.this, R.layout.list_item,
                    Ringtones));
           
            lv.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> aView, View v,
                        int position, long id) {
                    currentPosition = position;
                    playRingtone(DIRECTORY+Ringtones.get(position));
                }
            });
        }
    }
   
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
       
        int NONE = Menu.NONE;
        if (mp.isPlaying()) {
            menu.add(NONE, STOP, 0, "Stop");
        }
        menu.add(NONE, EXIT, 1, "Exit");
        return true;
    }
   
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case STOP:
            mp.stop();
            break;
        case EXIT:
            quit();
            break;
        }

        return super.onOptionsItemSelected(item);
    }

    private void playRingtone(String ringtone) {
        try {
            mp.reset();
            mp.setDataSource(ringtone);
            mp.prepare();
            mp.start();
        } catch (IOException e) {
            Log.v("Ringtone", e.getMessage());
        }
    }

    private List<String> FindFiles(Boolean fullPath) {
        final List<String> tFileList = new ArrayList<String>();
        Resources resources = getResources();
        // array of valid audio file extensions
        String[] audioTypes = resources.getStringArray(R.array.audio);
        FilenameFilter[] filter = new FilenameFilter[audioTypes.length];

        int i = 0;
        for (final String type : audioTypes) {
            filter[i] = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.endsWith("." + type);
                }
            };
            i++;
        }

        FileUtils fileUtils = new FileUtils();
        File[] allMatchingFiles = fileUtils.listFilesAsArray(
                new File(DIRECTORY), filter, -1);
        for (File f : allMatchingFiles) {
            if (fullPath) {
                tFileList.add(f.getAbsolutePath());
            }
            else {
                tFileList.add(f.getName());
            }
        }
        return tFileList;
    }

    public class FileUtils {

        public void saveArray(String filename, List<String> output_field) {
            try {
                FileOutputStream fos = new FileOutputStream(filename);
                GZIPOutputStream gzos = new GZIPOutputStream(fos);
                ObjectOutputStream out = new ObjectOutputStream(gzos);
                out.writeObject(output_field);
                out.flush();
                out.close();
            } catch (IOException e) {
                e.getStackTrace();
            }
        }

        @SuppressWarnings("unchecked")
        public List<String> loadArray(String filename) {
            try {
                FileInputStream fis = new FileInputStream(filename);
                GZIPInputStream gzis = new GZIPInputStream(fis);
                ObjectInputStream in = new ObjectInputStream(gzis);
                List<String> read_field = (List<String>) in.readObject();
                in.close();
                return read_field;
            } catch (Exception e) {
                e.getStackTrace();
            }
            return null;
        }

        public File[] listFilesAsArray(File directory, FilenameFilter[] filter,
                int recurse) {
            Collection<File> files = listFiles(directory, filter, recurse);

            File[] arr = new File[files.size()];
            return files.toArray(arr);
        }

        public Collection<File> listFiles(File directory,
                FilenameFilter[] filter, int recurse) {

            Vector<File> files = new Vector<File>();

            File[] entries = directory.listFiles();

            if (entries != null) {
                for (File entry : entries) {
                    for (FilenameFilter filefilter : filter) {
                        if (filter == null
                                || filefilter
                                        .accept(directory, entry.getName())) {
                            files.add(entry);
                            Log.v("FileUtils", "Added: "
                                    + entry.getName());
                        }
                    }
                    if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) {
                        recurse--;
                        files.addAll(listFiles(entry, filter, recurse));
                        recurse++;
                    }
                }
            }
            return files;
        }
    }
   
    public void quit() {
        int pid = android.os.Process.myPid();
        android.os.Process.killProcess(pid);
        System.exit(0);
    }
}

Here is a screenshot:

You can download the Eclipse Project Here: Download FindFilesByType.zip