diff --git a/res/layout/song_selector.xml b/res/layout/song_selector.xml index 66ce897f..01356dc5 100644 --- a/res/layout/song_selector.xml +++ b/res/layout/song_selector.xml @@ -22,16 +22,16 @@ android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="fill_parent"> - - + + android:fastScrollEnabled="true" /> + * + * This file is part of Vanilla Music Player. + * + * Vanilla Music Player is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Vanilla Music Player is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kreed.vanilla; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.TextView; + +public abstract class AbstractAdapter extends BaseAdapter implements Filterable { + protected Context mContext; + private List mObjects; + private Song[] mAllObjects; + private ArrayFilter mFilter; + protected float mSize; + protected int mPadding; + + public AbstractAdapter(Context context, Song[] allObjects) + { + mContext = context; + mAllObjects = allObjects; + + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + mSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, metrics); + mPadding = (int)mSize / 2; + } + + @Override + public boolean hasStableIds() + { + return true; + } + + protected void updateViews(Song song, TextView upper, TextView lower) + { + } + + public Filter getFilter() + { + if (mFilter == null) + mFilter = new ArrayFilter(); + return mFilter; + } + + private static final String[] mRanges = { ".", "[2abc]", "[3def]", "[4ghi]", "[5jkl]", "[6mno]", "[7pqrs]", "[8tuv]", "[9wxyz]"}; + private static Matcher createMatcher(String input) + { + String patternString = ""; + for (int i = 0, end = input.length(); i != end; ++i) { + char c = input.charAt(i); + int value = c - '1'; + if (value >= 0 && value < 9) + patternString += mRanges[value]; + else + patternString += c; + } + + return Pattern.compile(patternString, Pattern.CASE_INSENSITIVE).matcher(""); + } + + private class ArrayFilter extends Filter { + @Override + protected FilterResults performFiltering(CharSequence filter) + { + FilterResults results = new FilterResults(); + + if (filter == null || filter.length() == 0) { + results.values = Arrays.asList(mAllObjects); + results.count = mAllObjects.length; + } else { + String[] words = filter.toString().split("\\s+"); + Matcher[] matchers = new Matcher[words.length]; + for (int i = words.length; --i != -1; ) + matchers[i] = createMatcher(words[i]); + + int count = mAllObjects.length; + ArrayList newValues = new ArrayList(); + newValues.ensureCapacity(count); + + outer: + for (int i = 0; i != count; ++i) { + Song song = mAllObjects[i]; + + for (int j = matchers.length; --j != -1; ) { + if (song.artist != null && matchers[j].reset(song.artist).find()) + continue; + if (song.album != null && matchers[j].reset(song.album).find()) + continue; + if (song.title != null && matchers[j].reset(song.title).find()) + continue; + continue outer; + } + + newValues.add(song); + } + + newValues.trimToSize(); + + results.values = newValues; + results.count = newValues.size(); + } + + return results; + } + + @SuppressWarnings("unchecked") + @Override + protected void publishResults(CharSequence constraint, FilterResults results) + { + mObjects = (List)results.values; + if (results.count == 0) + notifyDataSetInvalidated(); + else + notifyDataSetChanged(); + } + } + + public int getCount() + { + if (mObjects == null) { + if (mAllObjects == null) + return 0; + return mAllObjects.length; + } + return mObjects.size(); + } + + public Song get(int i) + { + if (mObjects == null) { + if (mAllObjects == null) + return null; + return mAllObjects[i]; + } + return mObjects.get(i); + } + + public Object getItem(int i) + { + return get(i); + } + + public long getItemId(int i) + { + if (mObjects != null && mObjects.isEmpty()) + return 0; + return get(i).id; + } +} \ No newline at end of file diff --git a/src/org/kreed/vanilla/AlbumAdapter.java b/src/org/kreed/vanilla/AlbumAdapter.java new file mode 100644 index 00000000..6eaf2c87 --- /dev/null +++ b/src/org/kreed/vanilla/AlbumAdapter.java @@ -0,0 +1,64 @@ +package org.kreed.vanilla; + +import java.util.Arrays; +import java.util.HashMap; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class AlbumAdapter extends AbstractAdapter { + private static Song[] filter(Song[] songs) + { + HashMap albums = new HashMap(); + for (int i = songs.length; --i != -1; ) { + Song song = songs[i]; + if (!albums.containsKey(song.albumId)) + albums.put(song.albumId, song); + } + Song[] result = albums.values().toArray(new Song[0]); + Arrays.sort(result, new Song.AlbumComparator()); + return result; + } + + public AlbumAdapter(Context context, Song[] allSongs) + { + super(context, filter(allSongs)); + } + + public View getView(int position, View convertView, ViewGroup parent) + { + LinearLayout view = null; + try { + view = (LinearLayout)convertView; + } catch (ClassCastException e) { + } + + if (view == null) { + view = new LinearLayout(mContext); + view.setOrientation(LinearLayout.VERTICAL); + view.setPadding(mPadding, mPadding, mPadding, mPadding); + + TextView title = new TextView(mContext); + title.setSingleLine(); + title.setTextColor(Color.WHITE); + title.setTextSize(mSize); + title.setId(0); + view.addView(title); + + TextView artist = new TextView(mContext); + artist.setSingleLine(); + artist.setTextSize(mSize); + artist.setId(1); + view.addView(artist); + } + + Song song = get(position); + ((TextView)view.findViewById(0)).setText(song.album); + ((TextView)view.findViewById(1)).setText(song.artist); + return view; + } +} \ No newline at end of file diff --git a/src/org/kreed/vanilla/ArtistAdapter.java b/src/org/kreed/vanilla/ArtistAdapter.java new file mode 100644 index 00000000..26668d40 --- /dev/null +++ b/src/org/kreed/vanilla/ArtistAdapter.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Christopher Eby + * + * This file is part of Vanilla Music Player. + * + * Vanilla Music Player is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Vanilla Music Player is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.kreed.vanilla; + +import java.util.Arrays; +import java.util.HashMap; + +import android.content.Context; +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class ArtistAdapter extends AbstractAdapter { + private static Song[] filter(Song[] songs) + { + HashMap artists = new HashMap(); + for (int i = songs.length; --i != -1; ) { + Song song = songs[i]; + if (!artists.containsKey(song.artistId)) + artists.put(song.artistId, song); + } + Song[] result = artists.values().toArray(new Song[0]); + Arrays.sort(result, new Song.ArtistComparator()); + return result; + } + + public ArtistAdapter(Context context, Song[] allSongs) + { + super(context, filter(allSongs)); + } + + public View getView(int position, View convertView, ViewGroup parent) + { + TextView view = null; + try { + view = (TextView)convertView; + } catch (ClassCastException e) { + } + + if (view == null) { + view = new TextView(mContext); + view.setPadding(mPadding, mPadding, mPadding, mPadding); + view.setSingleLine(); + view.setTextColor(Color.WHITE); + view.setTextSize(mSize); + } + + view.setText(get(position).artist); + return view; + } +} \ No newline at end of file diff --git a/src/org/kreed/vanilla/Song.java b/src/org/kreed/vanilla/Song.java index ed8d77d6..5b77acdf 100644 --- a/src/org/kreed/vanilla/Song.java +++ b/src/org/kreed/vanilla/Song.java @@ -195,4 +195,18 @@ public class Song implements Parcelable { return a.title.compareTo(b.title); } } + + public static class AlbumComparator implements Comparator { + public int compare(Song a, Song b) + { + return a.album.compareTo(b.album); + } + } + + public static class ArtistComparator implements Comparator { + public int compare(Song a, Song b) + { + return a.artist.compareTo(b.artist); + } + } } \ No newline at end of file diff --git a/src/org/kreed/vanilla/SongAdapter.java b/src/org/kreed/vanilla/SongAdapter.java index c8664e22..3bb05d77 100644 --- a/src/org/kreed/vanilla/SongAdapter.java +++ b/src/org/kreed/vanilla/SongAdapter.java @@ -18,47 +18,25 @@ package org.kreed.vanilla; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import android.content.Context; import android.graphics.Color; -import android.util.DisplayMetrics; -import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.Filter; -import android.widget.Filterable; import android.widget.LinearLayout; import android.widget.TextView; -public class SongAdapter extends BaseAdapter implements Filterable { - private Context mContext; - private List mObjects; - private Song[] mAllObjects; - private ArrayFilter mFilter; - private float mSize; - private int mPadding; - - public SongAdapter(Context context) +public class SongAdapter extends AbstractAdapter { + private static Song[] sort(Song[] songs) { - mContext = context; - mAllObjects = Song.getAllSongMetadata(); - Arrays.sort(mAllObjects, new Song.TitleComparator()); - - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - mSize = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, metrics); - mPadding = (int)mSize / 2; + Arrays.sort(songs, new Song.TitleComparator()); + return songs; } - @Override - public boolean hasStableIds() + public SongAdapter(Context context, Song[] allSongs) { - return true; + super(ContextApplication.getContext(), sort(allSongs)); } public View getView(int position, View convertView, ViewGroup parent) @@ -88,120 +66,9 @@ public class SongAdapter extends BaseAdapter implements Filterable { view.addView(artist); } - ((TextView)view.findViewById(0)).setText(mObjects.get(position).title); - ((TextView)view.findViewById(1)).setText(mObjects.get(position).artist); + Song song = get(position); + ((TextView)view.findViewById(0)).setText(song.title); + ((TextView)view.findViewById(1)).setText(song.artist); return view; } - - public Filter getFilter() - { - if (mFilter == null) - mFilter = new ArrayFilter(); - return mFilter; - } - - private static final String[] mRanges = { ".", "[2abc]", "[3def]", "[4ghi]", "[5jkl]", "[6mno]", "[7pqrs]", "[8tuv]", "[9wxyz]"}; - private static Matcher createMatcher(String input) - { - String patternString = ""; - for (int i = 0, end = input.length(); i != end; ++i) { - char c = input.charAt(i); - int value = c - '1'; - if (value >= 0 && value < 9) - patternString += mRanges[value]; - else - patternString += c; - } - - return Pattern.compile(patternString, Pattern.CASE_INSENSITIVE).matcher(""); - } - - private class ArrayFilter extends Filter { - @Override - protected FilterResults performFiltering(CharSequence filter) - { - FilterResults results = new FilterResults(); - - if (filter == null || filter.length() == 0) { - results.values = Arrays.asList(mAllObjects); - results.count = mAllObjects.length; - } else { - String[] words = filter.toString().split("\\s+"); - Matcher[] matchers = new Matcher[words.length]; - for (int i = words.length; --i != -1; ) - matchers[i] = createMatcher(words[i]); - - int count = mAllObjects.length; - ArrayList newValues = new ArrayList(); - newValues.ensureCapacity(count); - - outer: - for (int i = 0; i != count; ++i) { - Song song = mAllObjects[i]; - - for (int j = matchers.length; --j != -1; ) { - if (song.artist != null && matchers[j].reset(song.artist).find()) - continue; - if (song.album != null && matchers[j].reset(song.album).find()) - continue; - if (song.title != null && matchers[j].reset(song.title).find()) - continue; - continue outer; - } - - newValues.add(song); - } - - newValues.trimToSize(); - - results.values = newValues; - results.count = newValues.size(); - } - - return results; - } - - @SuppressWarnings("unchecked") - @Override - protected void publishResults(CharSequence constraint, FilterResults results) - { - mObjects = (List)results.values; - if (results.count == 0) - notifyDataSetInvalidated(); - else - notifyDataSetChanged(); - } - } - - public int getCount() - { - if (mObjects == null) { - if (mAllObjects == null) - return 0; - return mAllObjects.length; - } - return mObjects.size(); - } - - public Object getItem(int position) - { - if (mObjects == null) { - if (mAllObjects == null) - return 0; - return mAllObjects[position]; - } - return mObjects.get(position); - } - - public long getItemId(int position) - { - if (mObjects == null) { - if (mAllObjects == null) - return 0; - return mAllObjects[position].id; - } - if (mObjects.isEmpty()) - return 0; - return mObjects.get(position).id; - } } \ No newline at end of file diff --git a/src/org/kreed/vanilla/SongSelector.java b/src/org/kreed/vanilla/SongSelector.java index 1aae49cb..bb408592 100644 --- a/src/org/kreed/vanilla/SongSelector.java +++ b/src/org/kreed/vanilla/SongSelector.java @@ -39,9 +39,11 @@ import android.widget.TextView; public class SongSelector extends TabActivity implements AdapterView.OnItemClickListener, TextWatcher, View.OnClickListener { private TabHost mTabHost; - private SongAdapter mAdapter; - private ListView mListView; - private TextView mTextView; + private TextView mTextFilter; + + private AbstractAdapter mArtistAdapter; + private AbstractAdapter mAlbumAdapter; + private AbstractAdapter mSongAdapter; @Override public void onCreate(Bundle icicle) @@ -59,14 +61,23 @@ public class SongSelector extends TabActivity implements AdapterView.OnItemClick mTabHost.addTab(mTabHost.newTabSpec("tab_albums").setIndicator(res.getText(R.string.albums), res.getDrawable(R.drawable.tab_albums)).setContent(R.id.album_list)); mTabHost.addTab(mTabHost.newTabSpec("tab_songs").setIndicator(res.getText(R.string.songs), res.getDrawable(R.drawable.tab_songs)).setContent(R.id.song_list)); - mAdapter = new SongAdapter(this); + Song[] songs = Song.getAllSongMetadata(); + mArtistAdapter = new ArtistAdapter(this, songs); + mAlbumAdapter = new AlbumAdapter(this, songs); + mSongAdapter = new SongAdapter(this, songs); - mListView = (ListView)findViewById(R.id.song_list); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(this); + ListView artistView = (ListView)findViewById(R.id.artist_list); + artistView.setAdapter(mArtistAdapter); - mTextView = (TextView)findViewById(R.id.filter_text); - mTextView.addTextChangedListener(this); + ListView albumView = (ListView)findViewById(R.id.album_list); + albumView.setAdapter(mAlbumAdapter); + + ListView songView = (ListView)findViewById(R.id.song_list); + songView.setAdapter(mSongAdapter); + songView.setOnItemClickListener(this); + + mTextFilter = (TextView)findViewById(R.id.filter_text); + mTextFilter.addTextChangedListener(this); View clearButton = findViewById(R.id.clear_button); clearButton.setOnClickListener(this); @@ -79,7 +90,7 @@ public class SongSelector extends TabActivity implements AdapterView.OnItemClick inputType = InputType.TYPE_TEXT_VARIATION_FILTER; else inputType = InputType.TYPE_CLASS_TEXT; - mTextView.setInputType(inputType); + mTextFilter.setInputType(inputType); } @Override @@ -88,8 +99,8 @@ public class SongSelector extends TabActivity implements AdapterView.OnItemClick if (super.onKeyDown(keyCode, event)) return true; - mTextView.requestFocus(); - return mTextView.onKeyDown(keyCode, event); + mTextFilter.requestFocus(); + return mTextFilter.onKeyDown(keyCode, event); } public void onItemClick(AdapterView list, View view, int pos, long id) @@ -109,11 +120,11 @@ public class SongSelector extends TabActivity implements AdapterView.OnItemClick public void onTextChanged(CharSequence s, int start, int before, int count) { - mAdapter.getFilter().filter(s); + mSongAdapter.getFilter().filter(s); } public void onClick(View view) { - mTextView.setText(""); + mTextFilter.setText(""); } } \ No newline at end of file