switch to alsa.omap3 module
[android_pandora.git] / apps / oi-filemanager / FileManager / src / org / openintents / filemanager / DirectoryScanner.java
CommitLineData
811a5a4a 1package org.openintents.filemanager;
2
3import java.io.File;
4import java.lang.reflect.Method;
5import java.util.ArrayList;
6import java.util.Collections;
7import java.util.Comparator;
8import java.util.List;
9
10import org.openintents.filemanager.util.FileUtils;
11import org.openintents.filemanager.util.ImageUtils;
12import org.openintents.filemanager.util.MimeTypes;
13
14import android.content.Context;
15import android.content.Intent;
16import android.content.pm.ApplicationInfo;
17import android.content.pm.PackageInfo;
18import android.content.pm.PackageManager;
19import android.content.pm.PackageManager.NameNotFoundException;
20import android.content.pm.ResolveInfo;
21import android.content.res.Resources.NotFoundException;
22import android.graphics.drawable.Drawable;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.Message;
26import android.os.SystemClock;
27import android.support.v2.os.Build;
28import android.util.Log;
29
30public class DirectoryScanner extends Thread {
31
32 private static final String TAG = "OIFM_DirScanner";
33
34 private File currentDirectory;
35 boolean cancel;
36
37 private String mSdCardPath;
38 private Context context;
39 private MimeTypes mMimeTypes;
40 private Handler handler;
41 private long operationStartTime;
42 private String mFilterFiletype;
43 private String mFilterMimetype;
44
45 private boolean mWriteableOnly;
46
47 private boolean mDirectoriesOnly;
48
49 // Update progress bar every n files
50 static final private int PROGRESS_STEPS = 50;
51
52 // APK MIME type
53 private static final String MIME_APK = "application/vnd.android.package-archive";
54
55 // Cupcake-specific methods
56 static Method formatter_formatFileSize;
57
58 static {
59 initializeCupcakeInterface();
60 }
61
62
63
64 DirectoryScanner(File directory, Context context, Handler handler, MimeTypes mimeTypes, String filterFiletype, String filterMimetype, String sdCardPath, boolean writeableOnly, boolean directoriesOnly) {
65 super("Directory Scanner");
66 currentDirectory = directory;
67 this.context = context;
68 this.handler = handler;
69 this.mMimeTypes = mimeTypes;
70 this.mFilterFiletype = filterFiletype;
71 this.mFilterMimetype = filterMimetype;
72 this.mSdCardPath = sdCardPath;
73 this.mWriteableOnly = writeableOnly;
74 this.mDirectoriesOnly = directoriesOnly;
75 }
76
77 private void clearData() {
78 // Remove all references so we don't delay the garbage collection.
79 context = null;
80 mMimeTypes = null;
81 handler = null;
82 }
83
84 public void run() {
85 Log.v(TAG, "Scanning directory " + currentDirectory);
86
87 File[] files = currentDirectory.listFiles();
88
89 int fileCount = 0;
90 int dirCount = 0;
91 int sdCount = 0;
92 int totalCount = 0;
93
94 if (cancel) {
95 Log.v(TAG, "Scan aborted");
96 clearData();
97 return;
98 }
99
100 if (files == null) {
101 Log.v(TAG, "Returned null - inaccessible directory?");
102 totalCount = 0;
103 } else {
104 totalCount = files.length;
105 }
106
107 operationStartTime = SystemClock.uptimeMillis();
108
109 Log.v(TAG, "Counting files... (total count=" + totalCount + ")");
110
111 int progress = 0;
112
113 /** Dir separate for return after sorting*/
114 List<IconifiedText> listDir = new ArrayList<IconifiedText>(totalCount);
115 /** Dir separate for sorting */
116 List<File> listDirFile = new ArrayList<File>(totalCount);
117
118 /** Files separate for return after sorting*/
119 List<IconifiedText> listFile = new ArrayList<IconifiedText>(totalCount);
120 /** Files separate for sorting */
121 List<File> listFileFile = new ArrayList<File>(totalCount);
122
123 /** SD card separate for sorting - actually not sorted, so we don't need an ArrayList<File>*/
124 List<IconifiedText> listSdCard = new ArrayList<IconifiedText>(3);
125
126 boolean noMedia = false;
127
128 // Cache some commonly used icons.
129 Drawable sdIcon = context.getResources().getDrawable(R.drawable.ic_launcher_sdcard);
130 Drawable folderIcon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
131 Drawable genericFileIcon = context.getResources().getDrawable(R.drawable.icon_file);
132
133 Drawable currentIcon = null;
134
135 boolean displayHiddenFiles = PreferenceActivity.getDisplayHiddenFiles(context);
136
137 if (files != null) {
138 for (File currentFile : files){
139 if (cancel) {
140 // Abort!
141 Log.v(TAG, "Scan aborted while checking files");
142 clearData();
143 return;
144 }
145
146 progress++;
147 updateProgress(progress, totalCount);
148
149 //If the user doesn't want to display hidden files and the file is hidden,
150 //skip displaying the file
151 if (!displayHiddenFiles && currentFile.isHidden()){
152 continue;
153 }
154
155
156 if (currentFile.isDirectory()) {
157 if (currentFile.getAbsolutePath().equals(mSdCardPath)) {
158 currentIcon = sdIcon;
159
160 listSdCard.add(new IconifiedText(
161 currentFile.getName(), "", currentIcon));
162 } else {
163 if (!mWriteableOnly || currentFile.canWrite()){
164 listDirFile.add(currentFile);
165 }
166 }
167 }else{
168 String fileName = currentFile.getName();
169
170 // Is this the ".nomedia" file?
171 if (!noMedia) {
172 if (fileName.equalsIgnoreCase(".nomedia")) {
173 // It is!
174 noMedia = true;
175 }
176 }
177
178 String mimetype = mMimeTypes.getMimeType(fileName);
179
180 String filetype = FileUtils.getExtension(fileName);
181 boolean ext_allow = filetype.equalsIgnoreCase(mFilterFiletype) || mFilterFiletype == "";
182 boolean mime_allow = mFilterMimetype != null &&
183 (mimetype.contentEquals(mFilterMimetype) || mFilterMimetype.contentEquals("*/*") ||
184 mFilterFiletype == null);
185 if (!mDirectoriesOnly && (ext_allow || mime_allow)) {
186 listFileFile.add(currentFile);
187 }
188 }
189 }
190 }
191
192 Log.v(TAG, "Sorting results...");
193
194 //Collections.sort(mListSdCard);
195 int sortBy = PreferenceActivity.getSortBy(context);
196 boolean ascending = PreferenceActivity.getAscending(context);
197
198
199 Collections.sort(listDirFile, Comparators.getForDirectory(sortBy, ascending));
200 Collections.sort(listFileFile, Comparators.getForFile(sortBy, ascending));
201
202 for(File f : listDirFile){
203 listDir.add(new IconifiedText(
204 f.getName(), FileUtils.formatDate(context, f.lastModified()), folderIcon));
205 }
206
207 for(File currentFile : listFileFile){
208 String mimetype = mMimeTypes.getMimeType(currentFile.getName());
209 currentIcon = getDrawableForMimetype(currentFile, mimetype);
210 if (currentIcon == null) {
211 currentIcon = genericFileIcon;
212 } else {
213 int width = genericFileIcon.getIntrinsicWidth();
214 int height = genericFileIcon.getIntrinsicHeight();
215 // Resizing image.
216 currentIcon = ImageUtils.resizeDrawable(currentIcon, width, height);
217
218 }
219
220 String size = "";
221
222 try {
223 size = (String) formatter_formatFileSize.invoke(null, context, currentFile.length());
224 } catch (Exception e) {
225 // The file size method is probably null (this is most
226 // likely not a Cupcake phone), or something else went wrong.
227 // Let's fall back to something primitive, like just the number
228 // of KB.
229 size = Long.toString(currentFile.length() / 1024);
230 size +=" KB";
231
232 // Technically "KB" should come from a string resource,
233 // but this is just a Cupcake 1.1 callback, and KB is universal
234 // enough.
235 }
236
237 listFile.add(new IconifiedText(
238 currentFile.getName(), size + " , " + FileUtils.formatDate(
239 context, currentFile.lastModified()), currentIcon));
240 }
241
242 if (!cancel) {
243 Log.v(TAG, "Sending data back to main thread");
244
245 DirectoryContents contents = new DirectoryContents();
246
247 contents.listDir = listDir;
248 contents.listFile = listFile;
249 contents.listSdCard = listSdCard;
250 contents.noMedia = noMedia;
251
252 Message msg = handler.obtainMessage(FileManagerActivity.MESSAGE_SHOW_DIRECTORY_CONTENTS);
253 msg.obj = contents;
254 msg.sendToTarget();
255 }
256
257 clearData();
258 }
259
260 private void updateProgress(int progress, int maxProgress) {
261 // Only update the progress bar every n steps...
262 if ((progress % PROGRESS_STEPS) == 0) {
263 // Also don't update for the first second.
264 long curTime = SystemClock.uptimeMillis();
265
266 if (curTime - operationStartTime < 1000L) {
267 return;
268 }
269
270 // Okay, send an update.
271 Message msg = handler.obtainMessage(FileManagerActivity.MESSAGE_SET_PROGRESS);
272 msg.arg1 = progress;
273 msg.arg2 = maxProgress;
274 msg.sendToTarget();
275 }
276 }
277
278 /**
279 * Return the Drawable that is associated with a specific mime type
280 * for the VIEW action.
281 *
282 * @param mimetype
283 * @return
284 */
285 Drawable getDrawableForMimetype(File file, String mimetype) {
286 if (mimetype == null) {
287 return null;
288 }
289
290 PackageManager pm = context.getPackageManager();
291
292 // Returns the icon packaged in files with the .apk MIME type.
293 if(mimetype.equals(MIME_APK)){
294 String path = file.getPath();
295 PackageInfo pInfo = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
296 if (pInfo!=null) {
297 ApplicationInfo aInfo = pInfo.applicationInfo;
298
299 // Bug in SDK versions >= 8. See here: http://code.google.com/p/android/issues/detail?id=9151
300 if(Build.VERSION.SDK_INT >= 8){
301 aInfo.sourceDir = path;
302 aInfo.publicSourceDir = path;
303 }
304
305 return aInfo.loadIcon(pm);
306 }
307 }
308
309 int iconResource = mMimeTypes.getIcon(mimetype);
310 Drawable ret = null;
311 if(iconResource > 0){
312 try {
313 ret = pm.getResourcesForApplication(context.getPackageName()).getDrawable(iconResource);
314 }catch(NotFoundException e){}
315 catch(NameNotFoundException e){}
316 }
317
318 if(ret != null){
319 return ret;
320 }
321
322 Uri data = FileUtils.getUri(file);
323
324 Intent intent = new Intent(Intent.ACTION_VIEW);
325 //intent.setType(mimetype);
326
327 // Let's probe the intent exactly in the same way as the VIEW action
328 // is performed in FileManagerActivity.openFile(..)
329 intent.setDataAndType(data, mimetype);
330
331 final List<ResolveInfo> lri = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
332
333 if (lri != null && lri.size() > 0) {
334 //Log.i(TAG, "lri.size()" + lri.size());
335
336 // return first element
337 int index = 0;
338
339 // Actually first element should be "best match",
340 // but it seems that more recently installed applications
341 // could be even better match.
342 index = lri.size()-1;
343
344 final ResolveInfo ri = lri.get(index);
345 return ri.loadIcon(pm);
346 }
347
348 return null;
349 }
350
351 private static void initializeCupcakeInterface() {
352 try {
353 formatter_formatFileSize = Class.forName("android.text.format.Formatter").getMethod("formatFileSize", Context.class, long.class);
354 } catch (Exception ex) {
355 // This is not cupcake.
356 return;
357 }
358 }
359}
360
361/**
362 * The container class for all comparators.
363 */
364class Comparators{
365 public static final int NAME = 1;
366 public static final int SIZE = 2;
367 public static final int LAST_MODIFIED = 3;
368
369
370 public static Comparator<File> getForFile(int comparator, boolean ascending){
371 switch(comparator){
372 case NAME: return new NameComparator(ascending);
373 case SIZE: return new SizeComparator(ascending);
374 case LAST_MODIFIED: return new LastModifiedComparator(ascending);
375 default: return null;
376 }
377 }
378 public static Comparator<File> getForDirectory(int comparator, boolean ascending){
379 switch(comparator){
380 case NAME: return new NameComparator(ascending);
381 case SIZE: return new NameComparator(ascending); //Not a bug! Getting directory's size is verry slow
382 case LAST_MODIFIED: return new LastModifiedComparator(ascending);
383 default: return null;
384 }
385 }
386}
387
388
389abstract class FileComparator implements Comparator<File>{
390 protected boolean ascending = true;
391
392 public FileComparator(boolean asc){
393 ascending = asc;
394 }
395
396 public FileComparator(){
397 this(true);
398 }
399
400 public int compare(File f1, File f2){
401 return comp((ascending ? f1 : f2), (ascending ? f2 : f1));
402 }
403
404 protected abstract int comp(File f1, File f2);
405}
406
407class NameComparator extends FileComparator{
408 public NameComparator(boolean asc){
409 super(asc);
410 }
411
412 protected int comp(File f1, File f2) {
413 return f1.getName().toLowerCase().compareTo(f2.getName().toLowerCase());
414 }
415}
416
417class SizeComparator extends FileComparator{
418 public SizeComparator(boolean asc){
419 super(asc);
420 }
421
422 protected int comp(File f1, File f2) {
423 return ((Long)f1.length()).compareTo(f2.length());
424 }
425
426 /*//Very inefficient
427 private long getFileSize(File f){
428 if(f.isFile())
429 return f.length();
430 int ret = 0;
431 for(File file : f.listFiles())
432 ret += getFileSize(file);
433
434 return ret;
435 }
436 */
437}
438
439class LastModifiedComparator extends FileComparator{
440 public LastModifiedComparator(boolean asc){
441 super(asc);
442 }
443
444 protected int comp(File f1, File f2) {
445 return ((Long)f1.lastModified()).compareTo(f2.lastModified());
446 }
447}