/** * * TURTLE PLAYER * * Licensed under MIT & GPL * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE * OR OTHER DEALINGS IN THE SOFTWARE. * * More Information @ www.turtle-player.co.uk * * @author Simon Honegger (Hoene84) */ package com.turtleplayer.view; import android.view.View; import android.widget.ImageView; import android.widget.ListView; import com.turtleplayer.Player; import com.turtleplayer.TurtlePlayer; import com.turtleplayer.common.MatchFilterVisitor; import com.turtleplayer.model.*; import com.turtleplayer.persistance.framework.filter.*; import com.turtleplayer.persistance.turtle.db.TurtleDatabase; import com.turtleplayer.persistance.turtle.db.structure.Tables; import com.turtleplayer.persistance.turtle.filter.*; import com.turtleplayer.preferences.AbstractKey; import com.turtleplayer.preferences.Keys; import com.turtleplayer.preferences.Preferences; import com.turtleplayer.preferences.PreferencesObserver; import com.turtleplayer.presentation.InstanceFormatter; import com.turtleplayer.util.DefaultAdapter; import java.util.*; public abstract class FileChooser implements TurtleDatabase.DbObserver { public enum Mode { Album(com.turtleplayerv2.R.id.albumButton, com.turtleplayerv2.R.drawable.album48, com.turtleplayerv2.R.drawable.album48_active), Artist(com.turtleplayerv2.R.id.artistButton, com.turtleplayerv2.R.drawable.artist48, com.turtleplayerv2.R.drawable.artist48_active), Track(com.turtleplayerv2.R.id.trackButton, com.turtleplayerv2.R.drawable.track48, com.turtleplayerv2.R.drawable.track48_active), Genre(com.turtleplayerv2.R.id.genreButton, com.turtleplayerv2.R.drawable.genre48, com.turtleplayerv2.R.drawable.genre48_active), Dir(com.turtleplayerv2.R.id.dirButton, com.turtleplayerv2.R.drawable.dir48, com.turtleplayerv2.R.drawable.dir48_active); private Mode(int buttonId, int drawable, int drawableActive) { this.drawable = drawable; this.drawableActive = drawableActive; this.buttonId = buttonId; } private final int drawable; private final int drawableActive; private final int buttonId; } private Mode currMode; private final TurtleDatabase database; private final Player listActivity; private final Preferences preferences; final DefaultAdapter<Instance> listAdapter; final FilterListAdapter filterListAdapter; ListView filterList = null; private Set<Filter<? super Tables.Tracks>> filters = new HashSet<Filter<? super Tables.Tracks>>(); private Set<Filter<? super Tables.Tracks>> permanentFilters = new HashSet<Filter<? super Tables.Tracks>>(); private Map<Mode, List<Filter<? super Tables.Tracks>>> filtersAddWithMode = new HashMap<Mode, List<Filter<? super Tables.Tracks>>>(); public FileChooser(Mode currMode, final TurtlePlayer tp, Player listActivity) { for(Mode mode : Mode.values()){ filtersAddWithMode.put(mode, new ArrayList<Filter<? super Tables.Tracks>>()); } this.currMode = currMode; this.database = tp.db; this.preferences = tp.playlist.preferences; this.listActivity = listActivity; tp.playlist.preferences.addObserver(new PreferencesObserver() { public void changed(AbstractKey<?, ?> key) { if (key.equals(Keys.MEDIA_DIR)) { final String mediaDir = tp.playlist.preferences.get(Keys.MEDIA_DIR); getFilter().accept(new TurtleFilterVisitor<Tables.Tracks, Void>() { public Void visit(DirFilter dirFilter) { if(!dirFilter.filtersInPath(mediaDir)) { removeFilter(dirFilter); update(); } return null; } public <T, Z> Void visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter) { return null; } public Void visit(FilterSet<? super Tables.Tracks> filterSet) { return null; } public Void visit(NotFilter<? super Tables.Tracks> notFilter) { return null; } }); } } public String getId() { return "OutdatedFilterUpdater"; } }); filterList = (ListView) listActivity.findViewById (com.turtleplayerv2.R.id.filterlist); filterListAdapter = new FilterListAdapter(listActivity.getApplicationContext(), new ArrayList<Filter<? super Tables.Tracks>>(getFilters())) { @Override protected void removeFilter(final Filter<? super Tables.Tracks> filter) { FileChooser.this.removeFilter(filter); update(); } @Override protected void chooseFilter(Filter<? super Tables.Tracks> filter) { filterChoosen(filter); } }; filterList.setAdapter(filterListAdapter); listAdapter = new DefaultAdapter<Instance>( listActivity.getApplicationContext(), new ArrayList<Instance>(), listActivity, false, InstanceFormatter.SHORT); listActivity.setListAdapter(listAdapter); change(currMode, null, false); init(); } private void init() { database.addObserver(this); for (final Mode currMode : Mode.values()) { getButton(currMode).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { filters.clear(); filterListAdapter.clear(); for(Filter<? super Tables.Tracks> permanentFilter : permanentFilters) { filterListAdapter.add(permanentFilter); } List<Filter<? super Tables.Tracks>> filtersToRemove = new ArrayList<Filter<? super Tables.Tracks>>(); for(Filter<? super Tables.Tracks> filterAddedWithMode : filtersAddWithMode.get(currMode)) { if(!permanentFilters.contains(filterAddedWithMode)) { filtersToRemove.add(filterAddedWithMode); } } filtersAddWithMode.get(currMode).removeAll(filtersToRemove); change(currMode, null, false); } }); } } private Filter<Tables.Tracks> getFilter() { return new FilterSet<Tables.Tracks>(getFilters()); } private Set<Filter<? super Tables.Tracks>> getFilters() { Set<Filter<? super Tables.Tracks>> allFilters = new HashSet<Filter<? super Tables.Tracks>>(); allFilters.addAll(filters); allFilters.addAll(permanentFilters); return allFilters; } private DirFilter getDirFilter() { DirFilter deepestDirFilter = new DirFilter( Operator.EQ, preferences.get(Keys.MEDIA_DIR)); for(Filter<? super Tables.Tracks> filter : getFilters()) { final DirFilter finalDeepestDirFilter = deepestDirFilter; deepestDirFilter = filter.accept(new TurtleFilterVisitor<Tables.Tracks, DirFilter>() { public DirFilter visit(DirFilter dirFilter) { return finalDeepestDirFilter.getValue().contains(dirFilter.getValueWithoutWildcards()) ? finalDeepestDirFilter : new DirFilter(Operator.EQ, dirFilter.getValueWithoutWildcards()); } public DirFilter visit(NotFilter<? super Tables.Tracks> notFilter) { return finalDeepestDirFilter; } public <T, Z> DirFilter visit(FieldFilter<? super Tables.Tracks, Z, T> fieldFilter) { return finalDeepestDirFilter; } public DirFilter visit(FilterSet<? super Tables.Tracks> filterSet) { return finalDeepestDirFilter; } }); } return deepestDirFilter; } /** * @param selection * @return null if no track was selected, track if trak was selected */ public Track choose(Instance selection) { return selection.accept(new InstanceVisitor<Track>() { public Track visit(Track track) { return track; } public Track visit(SongDigest track) { Filter<Tables.SongsReadable> trackFilter = new FieldFilter<Tables.SongsReadable, Track, String>(Tables.SongsReadable.TITLE, Operator.EQ, track.getSongName()); return database.getTracks(new FilterSet<Tables.Tracks>(getFilter(), trackFilter)).iterator().next(); } public Track visit(Album album) { Filter<Tables.AlbumsReadable> filter = new FieldFilter<Tables.AlbumsReadable, Track, String>(Tables.AlbumsReadable.ALBUM, Operator.EQ, album.getAlbumId()); change(Mode.Track, filter, false); return null; } public Track visit(GenreDigest genre) { Filter<Tables.GenresReadable> filter = new FieldFilter<Tables.GenresReadable, Track, String>(Tables.GenresReadable.GENRE, Operator.EQ, genre.getGenreId()); change(Mode.Artist, filter, false); return null; } public Track visit(ArtistDigest artist) { Filter<Tables.ArtistsReadable> filter = new FieldFilter<Tables.ArtistsReadable, Track, String>(Tables.ArtistsReadable.ARTIST, Operator.EQ, artist.getArtistId()); change(Mode.Album, filter, false); return null; } public Track visit(FSobject FSobject) { Filter<Tables.FsObjects> filter = new DirFilter(Operator.LIKE, FSobject.getFullPath() + "/%"); change(Mode.Dir, filter, true); return null; } }); } /** * @param toMode * @param filter - filter to add, can be null */ public void change(Mode toMode, final Filter<? super Tables.Tracks> filter, boolean permanant) { if(filter != null) { filtersAddWithMode.get(currMode).add(filter); addFilter(filter, permanant); } currMode = toMode; for (final Mode aMode : Mode.values()) { final ImageView button = getButton(aMode); button.post(new Runnable() { public void run() { button.setImageResource(aMode.equals(currMode) ? aMode.drawableActive : aMode.drawable); } }); } update(); } public void removeFilter(final Filter<? super Tables.Tracks> filter) { filters.remove(filter); permanentFilters.remove(filter); filterListAdapter.remove(filter); } public void addFilter(final Filter<? super Tables.Tracks> filter, boolean permanant) { (permanant ? permanentFilters : filters).add(filter); filterList.post(new Runnable() { public void run() { filterListAdapter.add(filter); } }); } public void update() { new Thread(new Runnable() { public void run() { switch (currMode) { case Album: List<Instance> albums = new ArrayList<Instance>(database.getAlbums(getFilter())); albums.remove(AlbumDigest.NO_ALBUM); albums.addAll(database.getTracks(new FilterSet<Tables.Tracks>(getFilter(), new FieldFilter<Tables.Tracks, Album, String>(Tables.AlbumsReadable.ALBUM, Operator.EQ, "")))); listAdapter.replace(albums); break; case Artist: List<Instance> artists = new ArrayList<Instance>(database.getArtists(getFilter())); artists.remove(ArtistDigest.NO_ARTIST); artists.addAll(database.getTracks(new FilterSet<Tables.Tracks>(getFilter(), new FieldFilter<Tables.Tracks, Artist, String>(Tables.ArtistsReadable.ARTIST, Operator.EQ, "")))); listAdapter.replace(artists); break; case Genre: List<Instance> genres = new ArrayList<Instance>(database.getGenres(getFilter())); genres.remove(AlbumDigest.NO_ALBUM); genres.addAll(database.getTracks(new FilterSet<Tables.Tracks>(getFilter(), new FieldFilter<Tables.Tracks, Genre, String>(Tables.GenresReadable.GENRE, Operator.EQ, "")))); listAdapter.replace(genres); break; case Track: listAdapter.replace(database.getTracks(getFilter())); break; case Dir: List<Instance> instances = new ArrayList<Instance>(database.getDirList(getDirFilter())); instances.addAll(database.getTracks(getDirFilter())); listAdapter.replace(instances); break; default: throw new RuntimeException(currMode.name() + " not expexted here"); } } }).start(); } public void updated(final Instance instance) { if(!Player.Slides.PLAYLIST.equals(listActivity.getCurrSlide())) { return; } Instance instanceToAdd = instance.accept(new InstanceVisitor<Instance>() { public Instance visit(Track track) { if(getFilter().accept(new MatchFilterVisitor<Track, Tables.Tracks>(track))) { switch (currMode) { case Album: return track.GetAlbum() == AlbumDigest.NO_ALBUM ? track : track.GetAlbum(); case Artist: return track.GetArtist() == ArtistDigest.NO_ARTIST ? track : track.GetArtist(); case Genre: return track.GetGenre() == GenreDigest.NO_GENRE ? track : track.GetGenre(); case Track: return track; case Dir: return getDirFilter().accept(new MatchFilterVisitor<Track, Tables.Tracks>(track)) ? track : null; default: throw new RuntimeException(currMode.name() + " not expexted here"); } } return null; } public Instance visit(SongDigest track) { throw new RuntimeException("not supported yet"); } public Instance visit(Album album) { throw new RuntimeException("not supported yet"); } public Instance visit(GenreDigest genre) { throw new RuntimeException("not supported yet"); } public Instance visit(ArtistDigest artist) { throw new RuntimeException("not supported yet"); } public Instance visit(FSobject dir) { return getDirFilter().accept(new MatchFilterVisitor<FSobject, Tables.Dirs>(dir)) && Mode.Dir.equals(currMode) ? dir : null; } }); if(instanceToAdd != null) { listAdapter.add(instanceToAdd); } } public boolean back(){ Mode backMode; switch (currMode) { case Album: backMode = Mode.Artist; break; case Artist: backMode = Mode.Genre; break; case Genre: backMode = null; break; case Track: backMode = Mode.Album; break; case Dir: backMode = Mode.Dir; break; default: throw new RuntimeException(currMode.name() + " not expexted here"); } final List<Filter<? super Tables.Tracks>> filtersAddedByBack = filtersAddWithMode.get(backMode); if(backMode == null || filtersAddedByBack.isEmpty()) { return true; } else { final Filter<? super Tables.Tracks> filterAddedByBack = filtersAddedByBack.remove(filtersAddedByBack.size()-1); filterList.post(new Runnable() { public void run() { removeFilter(filterAddedByBack); } }); change(backMode, null, false); return false; } } public void cleared() { listAdapter.clear(); } public String getId() { return "FileChooserUpdater"; } private ImageView getButton(Mode mode) { return (ImageView) listActivity.findViewById(mode.buttonId); } protected abstract void filterChoosen(Filter<? super Tables.Tracks> filter); }