Added a music file filter and alphabetical sorting.

This commit is contained in:
Karim Abou Zeid 2016-03-18 11:24:15 +01:00
commit 5dc61b64ca
2 changed files with 109 additions and 37 deletions

View file

@ -25,6 +25,7 @@ import com.kabouzeid.gramophone.util.Util;
import java.io.File; import java.io.File;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File> { public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File> {
@ -32,20 +33,17 @@ public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewH
private static final int FOLDER = 1; private static final int FOLDER = 1;
private final AppCompatActivity activity; private final AppCompatActivity activity;
private ArrayList<File> dataSet; private List<File> dataSet;
private final int itemLayoutRes; private final int itemLayoutRes;
@Nullable @Nullable
private final Callbacks callbacks; private final Callbacks callbacks;
@Nullable
private final CabHolder cabHolder;
public SongFileAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList<File> dataSet, @LayoutRes int itemLayoutRes, @Nullable Callbacks callback, @Nullable CabHolder cabHolder) { public SongFileAdapter(@NonNull AppCompatActivity activity, @NonNull List<File> songFiles, @LayoutRes int itemLayoutRes, @Nullable Callbacks callback, @Nullable CabHolder cabHolder) {
super(activity, cabHolder, R.menu.menu_media_selection); super(activity, cabHolder, R.menu.menu_media_selection);
this.activity = activity; this.activity = activity;
this.dataSet = dataSet; this.dataSet = songFiles;
this.itemLayoutRes = itemLayoutRes; this.itemLayoutRes = itemLayoutRes;
this.callbacks = callback; this.callbacks = callback;
this.cabHolder = cabHolder;
setHasStableIds(true); setHasStableIds(true);
} }
@ -59,8 +57,8 @@ public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewH
return dataSet.get(position).hashCode(); return dataSet.get(position).hashCode();
} }
public void swapDataSet(@NonNull ArrayList<File> files) { public void swapDataSet(@NonNull List<File> songFiles) {
this.dataSet = files; this.dataSet = songFiles;
notifyDataSetChanged(); notifyDataSetChanged();
} }

View file

@ -18,6 +18,7 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.Toast; import android.widget.Toast;
import com.afollestad.materialcab.MaterialCab; import com.afollestad.materialcab.MaterialCab;
@ -44,12 +45,12 @@ import java.io.FileFilter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import hugo.weaving.DebugLog;
public class FolderFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener { public class FolderFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener {
public static final String TAG = FolderFragment.class.getSimpleName(); public static final String TAG = FolderFragment.class.getSimpleName();
@ -111,7 +112,7 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
} }
private void updateAdapter(File directory) { private void updateAdapter(File directory) {
ArrayList<File> files = loadFiles(directory); List<File> files = sort(listFiles(directory, getFileFilter()));
if (adapter == null) { if (adapter == null) {
adapter = new SongFileAdapter(getMainActivity(), files, R.layout.item_list, this, this); adapter = new SongFileAdapter(getMainActivity(), files, R.layout.item_list, this, this);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@ -128,16 +129,6 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
} }
} }
@NonNull
private ArrayList<File> loadFiles(File directory) {
ArrayList<File> fileList = new ArrayList<>();
File[] files = directory.listFiles();
if (files != null) {
Collections.addAll(fileList, files);
}
return fileList;
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_folder, container, false); View view = inflater.inflate(R.layout.fragment_folder, container, false);
@ -271,12 +262,12 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
if (file.isDirectory()) { if (file.isDirectory()) {
setCrumb(new BreadCrumbLayout.Crumb(file), true); setCrumb(new BreadCrumbLayout.Crumb(file), true);
} else { } else {
ArrayList<Song> songs = matchFilesWithMediaStore(listFilesIncludingSubFolders(file.getParentFile(), new FileFilter() { ArrayList<Song> songs = matchFilesWithMediaStore(sort(listFilesDeep(file.getParentFile(), new FileFilter() {
@Override @Override
public boolean accept(File pathname) { public boolean accept(File pathname) {
return !pathname.isDirectory(); return !pathname.isDirectory() && getFileFilter().accept(pathname);
} }
})); })));
int startIndex = -1; int startIndex = -1;
for (int i = 0; i < songs.size(); i++) { for (int i = 0; i < songs.size(); i++) {
@ -288,12 +279,11 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
if (startIndex > -1) { if (startIndex > -1) {
MusicPlayerRemote.openQueue(songs, startIndex, true); MusicPlayerRemote.openQueue(songs, startIndex, true);
} else { } else {
Toast.makeText(getActivity(), "Could not play selected file.", Toast.LENGTH_SHORT).show(); // TODO replace with proper text. Toast.makeText(getActivity(), "Selected file is not listed in the media store. You might have to scan the folder first.", Toast.LENGTH_SHORT).show(); // TODO replace with proper text.
} }
} }
} }
@DebugLog
@Nullable @Nullable
private static SortedCursor makeSongCursor(@NonNull final Context context, @Nullable final List<File> files) { private static SortedCursor makeSongCursor(@NonNull final Context context, @Nullable final List<File> files) {
String selection = null; String selection = null;
@ -331,21 +321,21 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
@Override @Override
public void onAddToPlaylist(ArrayList<File> files) { public void onAddToPlaylist(ArrayList<File> files) {
ArrayList<Song> songs = matchFilesWithMediaStore(listFilesIncludingSubFolders(files, null)); ArrayList<Song> songs = matchFilesWithMediaStore(sort(listFilesDeep(files, getFileFilter())));
if (!songs.isEmpty()) if (!songs.isEmpty())
AddToPlaylistDialog.create(songs).show(getFragmentManager(), "ADD_PLAYLIST"); AddToPlaylistDialog.create(songs).show(getFragmentManager(), "ADD_PLAYLIST");
} }
@Override @Override
public void onAddToCurrentPlaying(ArrayList<File> files) { public void onAddToCurrentPlaying(ArrayList<File> files) {
ArrayList<Song> songs = matchFilesWithMediaStore(listFilesIncludingSubFolders(files, null)); ArrayList<Song> songs = matchFilesWithMediaStore(sort(listFilesDeep(files, getFileFilter())));
if (!songs.isEmpty()) if (!songs.isEmpty())
MusicPlayerRemote.enqueue(songs); MusicPlayerRemote.enqueue(songs);
} }
@Override @Override
public void onDeleteFromDevice(ArrayList<File> files) { public void onDeleteFromDevice(ArrayList<File> files) {
ArrayList<Song> songs = matchFilesWithMediaStore(listFilesIncludingSubFolders(files, null)); ArrayList<Song> songs = matchFilesWithMediaStore(sort(listFilesDeep(files, getFileFilter())));
if (!songs.isEmpty()) if (!songs.isEmpty())
DeleteSongsDialog.create(songs).show(getFragmentManager(), "DELETE_SONGS"); DeleteSongsDialog.create(songs).show(getFragmentManager(), "DELETE_SONGS");
} }
@ -355,28 +345,42 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
} }
@NonNull @NonNull
private static List<File> listFilesIncludingSubFolders(@NonNull File dir, @Nullable FileFilter fileFilter) { private List<File> listFiles(@NonNull File directory, @Nullable FileFilter fileFilter) {
List<File> fileList = new LinkedList<>();
File[] found = directory.listFiles(fileFilter);
if (found != null) {
Collections.addAll(fileList, found);
}
return fileList;
}
@NonNull
private static List<File> listFilesDeep(@NonNull File directory, @Nullable FileFilter fileFilter) {
List<File> files = new LinkedList<>(); List<File> files = new LinkedList<>();
internalListFiles(files, dir, fileFilter); internalListFilesDeep(files, directory, fileFilter);
return files; return files;
} }
@NonNull @NonNull
private static List<File> listFilesIncludingSubFolders(@NonNull List<File> dirs, @Nullable FileFilter fileFilter) { private static List<File> listFilesDeep(@NonNull List<File> files, @Nullable FileFilter fileFilter) {
List<File> files = new LinkedList<>(); List<File> resFiles = new LinkedList<>();
for (File file : dirs) { for (File file : files) {
internalListFiles(files, file, fileFilter); if (file.isDirectory()) {
internalListFilesDeep(resFiles, file, fileFilter);
} else if (fileFilter == null || fileFilter.accept(file)) {
resFiles.add(file);
} }
return files; }
return resFiles;
} }
private static void internalListFiles(@NonNull Collection<File> files, @NonNull File directory, @Nullable FileFilter fileFilter) { private static void internalListFilesDeep(@NonNull Collection<File> files, @NonNull File directory, @Nullable FileFilter fileFilter) {
File[] found = directory.listFiles(fileFilter); File[] found = directory.listFiles(fileFilter);
if (found != null) { if (found != null) {
for (File file : found) { for (File file : found) {
if (file.isDirectory()) { if (file.isDirectory()) {
internalListFiles(files, file, fileFilter); internalListFilesDeep(files, file, fileFilter);
} else { } else {
files.add(file); files.add(file);
} }
@ -394,4 +398,74 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
} }
} }
private List<File> sort(List<File> files) {
Collections.sort(files, new FileSorter());
return files;
}
FileFilter audioFileFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() || fileIsMimeType(pathname, "audio/*", MimeTypeMap.getSingleton());
}
};
private FileFilter getFileFilter() {
return audioFileFilter;
}
static boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mimeTypeMap) {
if (mimeType == null || mimeType.equals("*/*")) {
return true;
} else {
// get the file mime type
String filename = file.toURI().toString();
int dotPos = filename.lastIndexOf('.');
if (dotPos == -1) {
return false;
}
String fileExtension = filename.substring(dotPos + 1);
String fileType = mimeTypeMap.getMimeTypeFromExtension(fileExtension);
if (fileType == null) {
return false;
}
// check the 'type/subtype' pattern
if (fileType.equals(mimeType)) {
return true;
}
// check the 'type/*' pattern
int mimeTypeDelimiter = mimeType.lastIndexOf('/');
if (mimeTypeDelimiter == -1) {
return false;
}
String mimeTypeMainType = mimeType.substring(0, mimeTypeDelimiter);
String mimeTypeSubtype = mimeType.substring(mimeTypeDelimiter + 1);
if (!mimeTypeSubtype.equals("*")) {
return false;
}
int fileTypeDelimiter = fileType.lastIndexOf('/');
if (fileTypeDelimiter == -1) {
return false;
}
String fileTypeMainType = fileType.substring(0, fileTypeDelimiter);
if (fileTypeMainType.equals(mimeTypeMainType)) {
return true;
}
}
return false;
}
private static class FileSorter implements Comparator<File> {
@Override
public int compare(File lhs, File rhs) {
if (lhs.isDirectory() && !rhs.isDirectory()) {
return -1;
} else if (!lhs.isDirectory() && rhs.isDirectory()) {
return 1;
} else {
return lhs.getName().compareToIgnoreCase(rhs.getName());
}
}
}
} }