Commit 282a1950 authored by Sébastien Toque's avatar Sébastien Toque

UI Audio: show albums hierarchically under artists & genre

parent b0e44b28
......@@ -63,20 +63,23 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fastScrollEnabled="true" />
<ListView
<ExpandableListView
android:id="@+id/artists_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:groupIndicator="@null"
android:fastScrollEnabled="true" />
<ListView
<ExpandableListView
android:id="@+id/albums_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:groupIndicator="@null"
android:fastScrollEnabled="true" />
<ListView
<ExpandableListView
android:id="@+id/genres_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:groupIndicator="@null"
android:fastScrollEnabled="true" />
</org.videolan.vlc.widget.FlingViewGroup>
</LinearLayout>
......@@ -2,29 +2,44 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical">
android:orientation="horizontal" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:text="@string/title"
android:textColor="#ffffff"
android:textSize="20dip" />
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:text="@string/title"
android:textColor="#ffffff"
android:textSize="20dip" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:text="@string/songs"
android:textColor="#888888"
android:textSize="14dip" />
</LinearLayout>
<TextView
android:id="@+id/text"
<ImageView
android:id="@+id/more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginBottom="5dip"
android:text="@string/songs"
android:textColor="#888888"
android:textSize="14dip" />
android:layout_gravity="center_vertical|right"
android:layout_marginRight="15dip"
android:src="@drawable/ic_down" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="25dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:text="@string/title"
android:textColor="#ffffff"
android:textSize="20dip" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="25dip"
android:layout_marginRight="5dip"
android:layout_marginBottom="5dip"
android:text="@string/songs"
android:textColor="#888888"
android:textSize="14dip" />
</LinearLayout>
\ No newline at end of file
......@@ -37,6 +37,11 @@
</plurals>
<string name="artists">Artistes</string>
<string name="albums">Albums</string>
<plurals name="albums">
<item quantity="one">1 album</item>
<item quantity="other">%d albums</item>
</plurals>
<string name="all_albums">Tous les albums</string>
<string name="genres">Genres</string>
<string name="playlists">Listes</string>
<string name="time_0">0:00</string>
......
......@@ -38,6 +38,11 @@
</plurals>
<string name="artists">Artists</string>
<string name="albums">Albums</string>
<plurals name="albums">
<item quantity="one">1 album</item>
<item quantity="other">%d albums</item>
</plurals>
<string name="all_albums">All albums</string>
<string name="genres">Genres</string>
<string name="playlists">Playlists</string>
<string name="time_0">0:00</string>
......
......@@ -99,7 +99,7 @@ public class MediaLibrary {
return audioItems;
}
public ArrayList<Media> getAudioItems(String name, int mode) {
public ArrayList<Media> getAudioItems(String name, String name2, int mode) {
ArrayList<Media> audioItems = new ArrayList<Media>();
for (int i = 0; i < mItemList.size(); i++) {
Media item = mItemList.get(i);
......@@ -108,13 +108,13 @@ public class MediaLibrary {
boolean valid = false;
switch (mode) {
case AudioBrowserActivity.MODE_ARTIST:
valid = name.equals(item.getArtist());
valid = name.equals(item.getArtist()) && (name2 == null || name2.equals(item.getAlbum()));
break;
case AudioBrowserActivity.MODE_ALBUM:
valid = name.equals(item.getAlbum());
break;
case AudioBrowserActivity.MODE_GENRE:
valid = name.equals(item.getGenre());
valid = name.equals(item.getGenre()) && (name2 == null || name2.equals(item.getAlbum()));
break;
default:
break;
......
......@@ -47,6 +47,10 @@ import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ListView;
......@@ -94,21 +98,24 @@ public class AudioBrowserActivity extends Activity implements ISortable {
mMediaLibrary.addUpdateHandler(mHandler);
mSongsAdapter = new AudioSongsListAdapter(this, R.layout.audio_browser_item);
mArtistsAdapter = new AudioPlaylistAdapter(this, R.layout.audio_browser_item);
mAlbumsAdapter = new AudioPlaylistAdapter(this, R.layout.audio_browser_item);
mGenresAdapter = new AudioPlaylistAdapter(this, R.layout.audio_browser_item);
mArtistsAdapter = new AudioPlaylistAdapter(this, R.plurals.albums, R.plurals.songs);
mAlbumsAdapter = new AudioPlaylistAdapter(this, R.plurals.songs, R.plurals.songs);
mGenresAdapter = new AudioPlaylistAdapter(this, R.plurals.albums, R.plurals.songs);
ListView songsList = (ListView) findViewById(R.id.songs_list);
ListView artistList = (ListView) findViewById(R.id.artists_list);
ListView albumList = (ListView) findViewById(R.id.albums_list);
ListView genreList = (ListView) findViewById(R.id.genres_list);
ExpandableListView artistList = (ExpandableListView) findViewById(R.id.artists_list);
ExpandableListView albumList = (ExpandableListView) findViewById(R.id.albums_list);
ExpandableListView genreList = (ExpandableListView) findViewById(R.id.genres_list);
songsList.setAdapter(mSongsAdapter);
artistList.setAdapter(mArtistsAdapter);
albumList.setAdapter(mAlbumsAdapter);
genreList.setAdapter(mGenresAdapter);
songsList.setOnItemClickListener(songListener);
artistList.setOnItemClickListener(playlistListener);
albumList.setOnItemClickListener(playlistListener);
genreList.setOnItemClickListener(playlistListener);
artistList.setOnGroupClickListener(playlistListener);
albumList.setOnGroupClickListener(playlistListener);
genreList.setOnGroupClickListener(playlistListener);
artistList.setOnChildClickListener(playlistChildListener);
albumList.setOnChildClickListener(playlistChildListener);
genreList.setOnChildClickListener(playlistChildListener);
songsList.setOnCreateContextMenuListener(contextMenuListener);
artistList.setOnCreateContextMenuListener(contextMenuListener);
albumList.setOnCreateContextMenuListener(contextMenuListener);
......@@ -127,16 +134,33 @@ public class AudioBrowserActivity extends Activity implements ISortable {
}
};
OnItemClickListener playlistListener = new OnItemClickListener() {
OnGroupClickListener playlistListener = new OnGroupClickListener() {
@Override
public void onItemClick(AdapterView<?> av, View v, int p, long id) {
AudioPlaylistAdapter adapter = (AudioPlaylistAdapter) av.getAdapter();
String name = adapter.getItem(p);
public boolean onGroupClick(ExpandableListView elv, View v, int groupPosition, long id) {
AudioPlaylistAdapter adapter = (AudioPlaylistAdapter) elv.getExpandableListAdapter();
if (adapter.getChildrenCount(groupPosition) > 2)
return false;
String name = adapter.getGroup(groupPosition);
Intent intent = new Intent(AudioBrowserActivity.this, AudioListActivity.class);
AudioListActivity.set(intent, name, null, mFlingViewGroup.getPosition());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
}
};
OnChildClickListener playlistChildListener = new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView elv, View v, int groupPosition, int childPosition, long id) {
AudioPlaylistAdapter adapter = (AudioPlaylistAdapter) elv.getExpandableListAdapter();
String name = adapter.getGroup(groupPosition);
String child = adapter.getChild(groupPosition, childPosition);
Intent intent = new Intent(AudioBrowserActivity.this, AudioListActivity.class);
AudioListActivity.set(intent, name, mFlingViewGroup.getPosition());
AudioListActivity.set(intent, name, child, mFlingViewGroup.getPosition());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return false;
}
};
......@@ -155,33 +179,48 @@ public class AudioBrowserActivity extends Activity implements ISortable {
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
int startPosition;
int groupPosition;
int childPosition;
List<String> medias;
int id = item.getItemId();
boolean play_all = id == MENU_PLAY_ALL || id == MENU_APPEND_ALL;
boolean play_append = id == MENU_APPEND || id == MENU_APPEND_ALL;
int start_position;
List<String> medias;
ContextMenuInfo menuInfo = item.getMenuInfo();
if (ExpandableListContextMenuInfo.class.isInstance(menuInfo)) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
if (childPosition < 0)
childPosition = 0;
}
else {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
groupPosition = info.position;
childPosition = 0;
}
if (play_all) {
start_position = menuInfo.position;
startPosition = groupPosition;
medias = mSongsAdapter.getLocations();
}
else {
start_position = 0;
startPosition = 0;
switch (mFlingViewGroup.getPosition())
{
case MODE_SONG:
medias = mSongsAdapter.getLocation(menuInfo.position);
medias = mSongsAdapter.getLocation(groupPosition);
break;
case MODE_ARTIST:
medias = mArtistsAdapter.getPlaylist(menuInfo.position);
medias = mArtistsAdapter.getPlaylist(groupPosition, childPosition);
break;
case MODE_ALBUM:
medias = mAlbumsAdapter.getPlaylist(menuInfo.position);
medias = mAlbumsAdapter.getPlaylist(groupPosition, childPosition);
break;
case MODE_GENRE:
medias = mGenresAdapter.getPlaylist(menuInfo.position);
medias = mGenresAdapter.getPlaylist(groupPosition, childPosition);
break;
default:
return true;
......@@ -190,7 +229,7 @@ public class AudioBrowserActivity extends Activity implements ISortable {
if (play_append)
mAudioController.append(medias);
else
mAudioController.load(medias, start_position);
mAudioController.load(medias, startPosition);
Intent intent = new Intent(AudioBrowserActivity.this, AudioPlayerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
......@@ -313,19 +352,21 @@ public class AudioBrowserActivity extends Activity implements ISortable {
Collections.sort(audioList, byArtist);
for (int i = 0; i < audioList.size(); i++) {
Media media = audioList.get(i);
mArtistsAdapter.add(media.getArtist(), media);
mArtistsAdapter.add(media.getArtist(), null, media);
mArtistsAdapter.add(media.getArtist(), media.getAlbum(), media);
}
Collections.sort(audioList, byAlbum);
for (int i = 0; i < audioList.size(); i++) {
Media media = audioList.get(i);
mAlbumsAdapter.add(media.getAlbum(), media);
mAlbumsAdapter.add(media.getAlbum(), null, media);
}
Collections.sort(audioList, byGenre);
for (int i = 0; i < audioList.size(); i++) {
Media media = audioList.get(i);
mGenresAdapter.add(media.getGenre(), media);
mGenresAdapter.add(media.getGenre(), null, media);
mGenresAdapter.add(media.getGenre(), media.getAlbum(), media);
}
mSongsAdapter.notifyDataSetChanged();
......
......@@ -59,6 +59,7 @@ public class AudioListActivity extends ListActivity {
private boolean mSortReverse = false;
private int mSortBy = SORT_BY_TITLE;
public final static String EXTRA_NAME = "name";
public final static String EXTRA_NAME2 = "name2";
public final static String EXTRA_MODE = "mode";
@Override
......@@ -99,8 +100,9 @@ public class AudioListActivity extends ListActivity {
super.onDestroy();
}
public static void set(Intent intent, String name, int mode) {
public static void set(Intent intent, String name, String name2, int mode) {
intent.putExtra(EXTRA_NAME, name);
intent.putExtra(EXTRA_NAME2, name2);
intent.putExtra(EXTRA_MODE, mode);
}
......@@ -187,6 +189,7 @@ public class AudioListActivity extends ListActivity {
private void updateList() {
String name = getIntent().getStringExtra(EXTRA_NAME);
String name2 = getIntent().getStringExtra(EXTRA_NAME2);
int mode = getIntent().getIntExtra(EXTRA_MODE, 0);
List<Media> audioList;
List<String> itemList;
......@@ -200,8 +203,8 @@ public class AudioListActivity extends ListActivity {
audioList = MediaLibrary.getInstance(this).getMediaItems(itemList);
}
else {
mTitle.setText(name);
audioList = MediaLibrary.getInstance(this).getAudioItems(name, mode);
mTitle.setText(name2 != null ? name2 : name);
audioList = MediaLibrary.getInstance(this).getAudioItems(name, name2, mode);
}
mSongsAdapter.clear();
......
......@@ -32,80 +32,203 @@ import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class AudioPlaylistAdapter extends ArrayAdapter<String> {
public class AudioPlaylistAdapter extends BaseExpandableListAdapter {
private Context mContext;
private LayoutInflater mInflater;
private final int mGroupTextId;
private final int mChildTextId;
private ArrayList<String> mTitles;
private HashMap<String, ArrayList<Media>> mPlaylists;
private HashMap<String, ArrayList<String>> mSubTitles;
private HashMap<String, HashMap<String, ArrayList<Media>>> mGroups;
public AudioPlaylistAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
public AudioPlaylistAdapter(Context context, int groupTextId, int childTextId) {
mContext = context;
mInflater = LayoutInflater.from(context);
mGroupTextId = groupTextId;
mChildTextId = childTextId;
mTitles = new ArrayList<String>();
mPlaylists = new HashMap<String, ArrayList<Media>>();
mSubTitles = new HashMap<String, ArrayList<String>>();
mGroups = new HashMap<String, HashMap<String, ArrayList<Media>>>();
}
public void add(String title, Media media) {
public void add(String title, String subtitle, Media media) {
ArrayList<String> subtitles;
HashMap<String, ArrayList<Media>> group;
ArrayList<Media> list;
if (!mTitles.contains(title)) {
list = new ArrayList<Media>();
mPlaylists.put(title, list);
if (!mSubTitles.containsKey(title)) {
subtitles = new ArrayList<String>();
group = new HashMap<String, ArrayList<Media>>();
mTitles.add(title);
super.add(title);
} else {
list = mPlaylists.get(title);
mSubTitles.put(title, subtitles);
mGroups.put(title, group);
}
else {
subtitles = mSubTitles.get(title);
group = mGroups.get(title);
}
if (!group.containsKey(subtitle)) {
list = new ArrayList<Media>();
subtitles.add(subtitle);
group.put(subtitle, list);
}
else {
list = group.get(subtitle);
}
list.add(media);
}
@Override
public void clear() {
for (String item : mTitles) {
mPlaylists.get(item).clear();
mPlaylists.remove(item);
ArrayList<String> subtitles = mSubTitles.get(item);
HashMap<String, ArrayList<Media>> subgroups = mGroups.get(item);
for (String subitem : subtitles) {
subgroups.get(subitem).clear();
subgroups.remove(subitem);
}
subtitles.clear();
mSubTitles.remove(item);
mGroups.remove(item);
}
mTitles.clear();
super.clear();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
public int getGroupCount() {
return mGroups.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public String getGroup(int groupPosition) {
return mTitles.get(groupPosition);
}
@Override
public int getChildrenCount(int groupPosition) {
String key = mTitles.get(groupPosition);
int count = mSubTitles.get(key).size();
return count > 2 ? count : 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public String getChild(int groupPosition, int childPosition) {
String key = mTitles.get(groupPosition);
return mSubTitles.get(key).get(childPosition);
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
GroupViewHolder holder;
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.audio_browser_playlist, parent, false);
holder = new ViewHolder();
v = mInflater.inflate(R.layout.audio_browser_playlist, parent, false);
holder = new GroupViewHolder();
holder.title = (TextView) v.findViewById(R.id.title);
holder.text = (TextView) v.findViewById(R.id.text);
holder.more = (ImageView) v.findViewById(R.id.more);
v.setTag(holder);
} else
holder = (ViewHolder) v.getTag();
holder = (GroupViewHolder) v.getTag();
String name = mTitles.get(groupPosition);
int count = mSubTitles.get(name).size();
int countMedia = mGroups.get(name).get(null).size();
Resources res = mContext.getResources();
String name = mTitles.get(position);
ArrayList<Media> list = mPlaylists.get(name);
holder.title.setText(name);
Resources res = getContext().getResources();
holder.text.setText(res.getQuantityString(R.plurals.songs, list.size(), list.size()));
if (count > 2)
holder.text.setText(res.getQuantityString(mGroupTextId, count - 1, count - 1));
else if (count == 2)
holder.text.setText(String.format("%s - %s",
mSubTitles.get(name).get(1),
res.getQuantityString(mChildTextId, countMedia, countMedia)));
else
holder.text.setText(res.getQuantityString(mChildTextId, countMedia, countMedia));
holder.more.setVisibility(count > 2 ? View.VISIBLE : View.GONE);
holder.more.setImageResource(isExpanded ? R.drawable.ic_up : R.drawable.ic_down);
return v;
}
static class ViewHolder {
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildViewHolder holder;
View v = convertView;
if (v == null) {
v = mInflater.inflate(R.layout.audio_browser_playlist_child, parent, false);
holder = new ChildViewHolder();
holder.title = (TextView) v.findViewById(R.id.title);
holder.text = (TextView) v.findViewById(R.id.text);
v.setTag(holder);
} else
holder = (ChildViewHolder) v.getTag();
String key = mTitles.get(groupPosition);
String name = mSubTitles.get(key).get(childPosition);
ArrayList<Media> list = mGroups.get(key).get(name);
int count = list.size();
Resources res = mContext.getResources();
if (name != null)
holder.title.setText(name);
else
holder.title.setText(R.string.all_albums);
holder.text.setText(res.getQuantityString(mChildTextId, count, count));
return v;
}
static class GroupViewHolder {
TextView title;