package org.openintents.filemanager.files;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.openintents.filemanager.PreferenceActivity;
import org.openintents.filemanager.R;
import org.openintents.filemanager.util.FileUtils;
import org.openintents.filemanager.util.MimeTypes;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
public class DirectoryScanner extends Thread {
/** List of contents is ready. */
public static final int MESSAGE_SHOW_DIRECTORY_CONTENTS = 500; // List of contents is ready, obj = DirectoryContents
public static final int MESSAGE_SET_PROGRESS = 501; // Set progress bar, arg1 = current value, arg2 = max value
private static final String TAG = "OIFM_DirScanner";
private File currentDirectory;
private boolean running = false;
boolean cancelled;
private String mSdCardPath;
private Context context;
private MimeTypes mMimeTypes;
private Handler handler;
private String mFilterFiletype;
private String mFilterMimetype;
private boolean mWriteableOnly;
private boolean mDirectoriesOnly;
// Update progress bar every n files
static final private int PROGRESS_STEPS = 50;
// Scan related variables.
private int totalCount, progress;
private long operationStartTime;
private boolean noMedia, displayHidden;
private Drawable sdIcon, folderIcon, genericFileIcon;
private File[] files;
/** We keep all these three instead of one, so that sorting is done separately on each. */
private List<FileHolder> listDir, listFile, listSdCard;
public DirectoryScanner(File directory, Context context, Handler handler, MimeTypes mimeTypes, String filterFiletype, String filterMimetype, boolean writeableOnly, boolean directoriesOnly) {
super("Directory Scanner");
currentDirectory = directory;
this.context = context;
this.handler = handler;
this.mMimeTypes = mimeTypes;
this.mFilterFiletype = filterFiletype;
this.mFilterMimetype = filterMimetype;
this.mSdCardPath = Environment.getExternalStorageDirectory().getAbsolutePath();
this.mWriteableOnly = writeableOnly;
this.mDirectoriesOnly = directoriesOnly;
}
private void init(){
Log.v(TAG, "Scanning directory " + currentDirectory);
if (cancelled) {
Log.v(TAG, "Scan aborted");
return;
}
totalCount = 0;
progress = 0;
files = currentDirectory.listFiles();
noMedia = false;
displayHidden = PreferenceActivity.getDisplayHiddenFiles(context);
sdIcon = context.getResources().getDrawable(R.drawable.ic_launcher_sdcard);
folderIcon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
genericFileIcon = context.getResources().getDrawable(R.drawable.ic_launcher_file);
operationStartTime = SystemClock.uptimeMillis();
if (files == null) {
Log.v(TAG, "Returned null - inaccessible directory?");
} else {
totalCount = files.length;
}
Log.v(TAG, "Total count=" + totalCount + ")");
/** Directory container */
listDir = new ArrayList<>(totalCount);
/** File container */
listFile = new ArrayList<>(totalCount);
/** External storage container*/
listSdCard = new ArrayList<>(3);
}
public void run() {
running = true;
init();
// Scan files
if (files != null) {
for (File currentFile : files){
if (cancelled) {
Log.v(TAG, "Scan aborted while checking files");
return;
}
progress++;
updateProgress(progress, totalCount);
// It's the noMedia file. Raise the flag.
if(currentFile.getName().equalsIgnoreCase(FileUtils.NOMEDIA_FILE_NAME))
noMedia = true;
//If the user doesn't want to display hidden files and the file is hidden, ignore this file.
if (!displayHidden && currentFile.isHidden()){
continue;
}
// It's a directory. Handle it.
if (currentFile.isDirectory()) {
// It's the sd card.
if (currentFile.getAbsolutePath().equals(mSdCardPath)) {
listSdCard.add(new FileHolder(currentFile, mMimeTypes.getMimeType(currentFile.getName()), sdIcon, context));
}
// It's a normal directory.
else {
if (!mWriteableOnly || currentFile.canWrite())
listDir.add(new FileHolder(currentFile, mMimeTypes.getMimeType(currentFile.getName()), folderIcon, context));
}
// It's a file. Handle it too :P
} else {
String fileName = currentFile.getName();
// Get the file's mimetype.
String mimetype = mMimeTypes.getMimeType(fileName);
String filetype = FileUtils.getExtension(fileName);
boolean ext_allow = filetype.equalsIgnoreCase(mFilterFiletype) || mFilterFiletype == "";
boolean mime_allow = mFilterMimetype != null &&
(mimetype.contentEquals(mFilterMimetype) || mFilterMimetype.contentEquals("*/*") ||
mFilterFiletype == null);
if (!mDirectoriesOnly && (ext_allow || mime_allow)) {
// Take advantage of the already parsed mimeType to set a specific icon.
listFile.add(new FileHolder(currentFile, mimetype, genericFileIcon, context));
}
}
}
}
Log.v(TAG, "Sorting results...");
int sortBy = PreferenceActivity.getSortBy(context);
boolean ascending = PreferenceActivity.getAscending(context);
// Sort lists
if (!cancelled) {
Collections.sort(listSdCard);
Collections.sort(listDir, Comparators.getForDirectory(sortBy, ascending));
Collections.sort(listFile, Comparators.getForFile(sortBy, ascending));
}
// Return lists
if (!cancelled) {
Log.v(TAG, "Sending data back to main thread");
DirectoryContents contents = new DirectoryContents();
contents.listDir = listDir;
contents.listFile = listFile;
contents.listSdCard = listSdCard;
contents.noMedia = noMedia;
Message msg = handler.obtainMessage(MESSAGE_SHOW_DIRECTORY_CONTENTS);
msg.obj = contents;
msg.sendToTarget();
}
running = false;
}
private void updateProgress(int progress, int maxProgress) {
// Only update the progress bar every n steps...
if ((progress % PROGRESS_STEPS) == 0) {
// Also don't update for the first second.
long curTime = SystemClock.uptimeMillis();
if (curTime - operationStartTime < 1000L) {
return;
}
// Okay, send an update.
Message msg = handler.obtainMessage(MESSAGE_SET_PROGRESS);
msg.arg1 = progress;
msg.arg2 = maxProgress;
msg.sendToTarget();
}
}
public void cancel(){
cancelled = true;
}
public boolean getNoMedia() {
return noMedia;
}
public boolean isRunning(){
return running;
}
}
/**
* The container class for all comparators.
*/
class Comparators{
public static final int NAME = 1;
public static final int SIZE = 2;
public static final int LAST_MODIFIED = 3;
public static final int EXTENSION = 4;
private Comparators() {
}
public static Comparator<FileHolder> getForFile(int comparator, boolean ascending){
switch(comparator){
case NAME: return new NameComparator(ascending);
case SIZE: return new SizeComparator(ascending);
case EXTENSION: return new ExtensionComparator(ascending);
case LAST_MODIFIED: return new LastModifiedComparator(ascending);
default: return null;
}
}
public static Comparator<FileHolder> getForDirectory(int comparator, boolean ascending){
switch(comparator){
case NAME: return new NameComparator(ascending);
case SIZE: return new NameComparator(ascending); //Not a bug! Getting directory's size is very slow
case EXTENSION: return new NameComparator(ascending); // Sorting by name as folders don't have extensions.
case LAST_MODIFIED: return new LastModifiedComparator(ascending);
default: return null;
}
}
}
abstract class FileHolderComparator implements Comparator<FileHolder>{
protected boolean ascending = true;
public FileHolderComparator(boolean asc){
ascending = asc;
}
public FileHolderComparator(){
this(true);
}
public int compare(FileHolder f1, FileHolder f2){
return comp((ascending ? f1 : f2), (ascending ? f2 : f1));
}
protected abstract int comp(FileHolder f1, FileHolder f2);
}
class NameComparator extends FileHolderComparator{
public NameComparator(boolean asc){
super(asc);
}
@Override
protected int comp(FileHolder f1, FileHolder f2) {
return f1.getName().toLowerCase().compareTo(f2.getName().toLowerCase());
}
}
class SizeComparator extends FileHolderComparator{
public SizeComparator(boolean asc){
super(asc);
}
@Override
protected int comp(FileHolder f1, FileHolder f2) {
return ((Long)f1.getFile().length()).compareTo(f2.getFile().length());
}
}
class ExtensionComparator extends FileHolderComparator{
public ExtensionComparator(boolean asc){
super(asc);
}
@Override
protected int comp(FileHolder f1, FileHolder f2) {
return f1.getExtension().compareTo(f2.getExtension());
}
}
class LastModifiedComparator extends FileHolderComparator{
public LastModifiedComparator(boolean asc){
super(asc);
}
@Override
protected int comp(FileHolder f1, FileHolder f2) {
return ((Long)f1.getFile().lastModified()).compareTo(f2.getFile().lastModified());
}
}