Artist album and artist song multi select

This commit is contained in:
Karim Abou Zeid 2015-05-25 18:11:51 +02:00
commit f033819620
5 changed files with 227 additions and 53 deletions

View file

@ -1,35 +1,44 @@
package com.kabouzeid.gramophone.adapter; package com.kabouzeid.gramophone.adapter;
import android.app.Activity; import android.support.annotation.Nullable;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.loader.AlbumSongLoader;
import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Album;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.NavigationUtil; import com.kabouzeid.gramophone.util.NavigationUtil;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
*/ */
public class ArtistAlbumAdapter extends RecyclerView.Adapter<ArtistAlbumAdapter.ViewHolder> { public class ArtistAlbumAdapter extends AbsMultiSelectAdapter<ArtistAlbumAdapter.ViewHolder, Album> {
public static final String TAG = AlbumAdapter.class.getSimpleName(); public static final String TAG = AlbumAdapter.class.getSimpleName();
private static final int TYPE_FIRST = 1; private static final int TYPE_FIRST = 1;
private static final int TYPE_MIDDLE = 2; private static final int TYPE_MIDDLE = 2;
private static final int TYPE_LAST = 3; private static final int TYPE_LAST = 3;
private final Activity activity; private final AppCompatActivity activity;
private final List<Album> dataSet; private final List<Album> dataSet;
private final int listMargin; private final int listMargin;
@ -61,6 +70,7 @@ public class ArtistAlbumAdapter extends RecyclerView.Adapter<ArtistAlbumAdapter.
holder.title.setText(album.title); holder.title.setText(album.title);
holder.year.setText(String.valueOf(album.year)); holder.year.setText(String.valueOf(album.year));
holder.view.setActivated(isChecked(album));
} }
@Override @Override
@ -77,32 +87,74 @@ public class ArtistAlbumAdapter extends RecyclerView.Adapter<ArtistAlbumAdapter.
} else return TYPE_MIDDLE; } else return TYPE_MIDDLE;
} }
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { @Override
protected Album getIdentifier(int position) {
return dataSet.get(position);
}
@Override
protected void onMultipleItemAction(MenuItem menuItem, ArrayList<Album> selection) {
switch (menuItem.getItemId()) {
case R.id.action_delete_from_disk:
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
break;
case R.id.action_add_to_playlist:
AddToPlaylistDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
break;
case R.id.action_add_to_current_playing:
MusicPlayerRemote.enqueue(getSongList(selection));
break;
}
}
private ArrayList<Song> getSongList(List<Album> albums) {
final ArrayList<Song> songs = new ArrayList<>();
for (Album album : albums) {
songs.addAll(AlbumSongLoader.getAlbumSongList(activity, album.id));
}
return songs;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final ImageView albumArt; final ImageView albumArt;
final TextView title; final TextView title;
final TextView year; final TextView year;
final View view;
public ViewHolder(View itemView) { public ViewHolder(View itemView) {
super(itemView); super(itemView);
view = itemView;
albumArt = (ImageView) itemView.findViewById(R.id.album_art); albumArt = (ImageView) itemView.findViewById(R.id.album_art);
title = (TextView) itemView.findViewById(R.id.album_title); title = (TextView) itemView.findViewById(R.id.album_title);
year = (TextView) itemView.findViewById(R.id.album_year); year = (TextView) itemView.findViewById(R.id.album_year);
itemView.setOnClickListener(this); view.setOnClickListener(this);
view.setOnLongClickListener(this);
} }
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Pair[] albumPairs = new Pair[]{ if (isInQuickSelectMode()) {
Pair.create(albumArt, toggleChecked(getAdapterPosition());
activity.getResources().getString(R.string.transition_album_cover) } else {
)}; Pair[] albumPairs = new Pair[]{
if (activity instanceof AbsFabActivity) Pair.create(albumArt,
albumPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(albumPairs); activity.getResources().getString(R.string.transition_album_cover)
NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition()).id, albumPairs); )};
if (activity instanceof AbsFabActivity)
albumPairs = ((AbsFabActivity) activity).getSharedViewsWithFab(albumPairs);
NavigationUtil.goToAlbum(activity, dataSet.get(getAdapterPosition()).id, albumPairs);
}
}
@Override
public boolean onLongClick(View view) {
toggleChecked(getAdapterPosition());
return true;
} }
} }
public ArtistAlbumAdapter(Activity activity, List<Album> objects) { public ArtistAlbumAdapter(AppCompatActivity activity, List<Album> objects, @Nullable CabHolder cabHolder) {
super(cabHolder, R.menu.menu_media_selection);
this.activity = activity; this.activity = activity;
dataSet = objects; dataSet = objects;
listMargin = activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin); listMargin = activity.getResources().getDimensionPixelSize(R.dimen.default_item_margin);

View file

@ -1,8 +1,10 @@
package com.kabouzeid.gramophone.adapter.songadapter; package com.kabouzeid.gramophone.adapter.songadapter;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -11,8 +13,11 @@ import android.widget.ImageView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
import com.afollestad.materialcab.MaterialCab;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper; import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity; import com.kabouzeid.gramophone.ui.activities.base.AbsFabActivity;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
@ -20,22 +25,30 @@ import com.kabouzeid.gramophone.util.NavigationUtil;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.List; import java.util.ArrayList;
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
*/ */
public class ArtistSongAdapter extends ArrayAdapter<Song> { public class ArtistSongAdapter extends ArrayAdapter<Song> implements MaterialCab.Callback {
private final CabHolder cabHolder;
private MaterialCab cab;
private ArrayList<Song> dataSet;
private ArrayList<Song> checked;
private final AppCompatActivity activity; private final AppCompatActivity activity;
public ArtistSongAdapter(AppCompatActivity activity, List<Song> songs) { public ArtistSongAdapter(AppCompatActivity activity, ArrayList<Song> songs, @Nullable CabHolder cabHolder) {
super(activity, R.layout.item_list_song, songs); super(activity, R.layout.item_list_song, songs);
this.activity = activity; this.activity = activity;
this.cabHolder = cabHolder;
checked = new ArrayList<>();
dataSet = songs;
} }
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(final int position, View convertView, ViewGroup parent) {
final Song song = getItem(position); final Song song = getItem(position);
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_list_artist_song, parent, false); convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_list_artist_song, parent, false);
@ -83,6 +96,84 @@ public class ArtistSongAdapter extends ArrayAdapter<Song> {
popupMenu.show(); popupMenu.show();
} }
}); });
convertView.setActivated(isChecked(song));
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (isInQuickSelectMode()) {
toggleChecked(song);
} else {
MusicPlayerRemote.openQueue(dataSet, position, true);
}
}
});
convertView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
toggleChecked(song);
return true;
}
});
return convertView; return convertView;
} }
private void onMultipleItemAction(MenuItem menuItem, ArrayList<Song> songs) {
}
protected void toggleChecked(Song song) {
if (cabHolder != null) {
openCabIfNecessary();
if (!checked.remove(song)) checked.add(song);
notifyDataSetChanged();
final int size = checked.size();
if (size <= 0) cab.finish();
else if (size == 1) cab.setTitle(checked.get(0).toString());
else if (size > 1) cab.setTitle(String.valueOf(size));
}
}
private void openCabIfNecessary() {
if (cabHolder != null) {
if (cab == null || !cab.isActive()) {
cab = cabHolder.openCab(R.menu.menu_media_selection, this);
}
}
}
private void uncheckAll() {
checked.clear();
notifyDataSetChanged();
}
protected boolean isChecked(Song song) {
return checked.contains(song);
}
protected boolean isInQuickSelectMode() {
return cab != null && cab.isActive();
}
@Override
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
return true;
}
@Override
public boolean onCabItemClicked(MenuItem menuItem) {
onMultipleItemAction(menuItem, new ArrayList<>(checked));
cab.finish();
uncheckAll();
return true;
}
@Override
public boolean onCabFinished(MaterialCab materialCab) {
uncheckAll();
return true;
}
} }

View file

@ -17,11 +17,11 @@ import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.afollestad.materialcab.MaterialCab;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.util.DialogUtils; import com.afollestad.materialdialogs.util.DialogUtils;
import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableListView;
@ -30,6 +30,7 @@ import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.ArtistAlbumAdapter; import com.kabouzeid.gramophone.adapter.ArtistAlbumAdapter;
import com.kabouzeid.gramophone.adapter.songadapter.ArtistSongAdapter; import com.kabouzeid.gramophone.adapter.songadapter.ArtistSongAdapter;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.PaletteColorHolder; import com.kabouzeid.gramophone.interfaces.PaletteColorHolder;
import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistBiographyLoader; import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistBiographyLoader;
import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistImageUrlLoader; import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistImageUrlLoader;
@ -62,7 +63,7 @@ import java.util.List;
* <p/> * <p/>
* Should be kinda stable ONLY AS IT IS!!! * Should be kinda stable ONLY AS IT IS!!!
*/ */
public class ArtistDetailActivity extends AbsFabActivity implements PaletteColorHolder { public class ArtistDetailActivity extends AbsFabActivity implements PaletteColorHolder, CabHolder {
public static final String TAG = ArtistDetailActivity.class.getSimpleName(); public static final String TAG = ArtistDetailActivity.class.getSimpleName();
private Artist artist; private Artist artist;
@ -73,10 +74,12 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor
private View songsBackgroundView; private View songsBackgroundView;
private TextView artistNameTv; private TextView artistNameTv;
private Toolbar toolbar; private Toolbar toolbar;
private MaterialCab cab;
private int headerOffset; private int headerOffset;
private int titleViewHeight; private int titleViewHeight;
private int artistImageViewHeight; private int artistImageViewHeight;
private int toolbarColor; private int toolbarColor;
private float toolbarAlpha;
private int bottomOffset; private int bottomOffset;
private View songListHeader; private View songListHeader;
@ -98,9 +101,9 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor
ViewHelper.setTranslationY(songsBackgroundView, Math.max(0, -scrollY + artistImageViewHeight)); ViewHelper.setTranslationY(songsBackgroundView, Math.max(0, -scrollY + artistImageViewHeight));
// Change alpha of overlay // Change alpha of overlay
float alpha = Math.max(0, Math.min(1, (float) scrollY / flexibleRange)); toolbarAlpha = Math.max(0, Math.min(1, (float) scrollY / flexibleRange));
ViewUtil.setBackgroundAlpha(toolbar, alpha, toolbarColor); ViewUtil.setBackgroundAlpha(toolbar, toolbarAlpha, toolbarColor);
ViewUtil.setBackgroundAlpha(statusBar, alpha, toolbarColor); ViewUtil.setBackgroundAlpha(statusBar, cab != null && cab.isActive() ? 1 : toolbarAlpha, toolbarColor);
// Translate name text // Translate name text
int maxTitleTranslationY = artistImageViewHeight; int maxTitleTranslationY = artistImageViewHeight;
@ -209,7 +212,7 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor
songListView.addHeaderView(songListHeader); songListView.addHeaderView(songListHeader);
final ArrayList<Song> songs = ArtistSongLoader.getArtistSongList(this, artist.id); final ArrayList<Song> songs = ArtistSongLoader.getArtistSongList(this, artist.id);
ArtistSongAdapter songAdapter = new ArtistSongAdapter(this, songs); ArtistSongAdapter songAdapter = new ArtistSongAdapter(this, songs, this);
songListView.setAdapter(songAdapter); songListView.setAdapter(songAdapter);
final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); final View contentView = getWindow().getDecorView().findViewById(android.R.id.content);
@ -220,23 +223,12 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor
observableScrollViewCallbacks.onScrollChanged(-(artistImageViewHeight + titleViewHeight), false, false); observableScrollViewCallbacks.onScrollChanged(-(artistImageViewHeight + titleViewHeight), false, false);
} }
}); });
songListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// header view has position 0
if (position == 0) {
return;
}
MusicPlayerRemote.openQueue(songs, position - 1, true);
}
});
} }
private void setUpAlbumRecyclerView() { private void setUpAlbumRecyclerView() {
albumRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); albumRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
List<Album> albums = ArtistAlbumLoader.getArtistAlbumList(this, artist.id); List<Album> albums = ArtistAlbumLoader.getArtistAlbumList(this, artist.id);
albumAdapter = new ArtistAlbumAdapter(this, albums); albumAdapter = new ArtistAlbumAdapter(this, albums, this);
albumRecyclerView.setAdapter(albumAdapter); albumRecyclerView.setAdapter(albumAdapter);
} }
@ -453,4 +445,31 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor
super.onDestroy(); super.onDestroy();
App.bus.unregister(this); App.bus.unregister(this);
} }
@Override
public MaterialCab openCab(int menuRes, final MaterialCab.Callback callback) {
if (cab != null && cab.isActive()) cab.finish();
cab = new MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setBackgroundColor(getPaletteColor())
.start(new MaterialCab.Callback() {
@Override
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
ViewUtil.setBackgroundAlpha(statusBar, 1, toolbarColor);
return callback.onCabCreated(materialCab, menu);
}
@Override
public boolean onCabItemClicked(MenuItem menuItem) {
return callback.onCabItemClicked(menuItem);
}
@Override
public boolean onCabFinished(MaterialCab materialCab) {
ViewUtil.setBackgroundAlpha(statusBar, toolbarAlpha, toolbarColor);
return callback.onCabFinished(materialCab);
}
});
return cab;
}
} }

View file

@ -67,10 +67,21 @@
android:layout_height="@dimen/statusMargin" android:layout_height="@dimen/statusMargin"
android:background="@android:color/transparent" /> android:background="@android:color/transparent" />
<android.support.v7.widget.Toolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
android:background="@android:color/transparent" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
android:background="@android:color/transparent" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</FrameLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="72dp"> android:layout_height="72dp"
android:foreground="?rect_selector">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants" android:descendantFocusability="blocksDescendants"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingStart="16dp"> android:paddingStart="16dp">
<com.kabouzeid.gramophone.views.SquareImageView <com.kabouzeid.gramophone.views.SquareImageView
android:id="@+id/album_art" android:id="@+id/album_art"
@ -30,30 +31,30 @@
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:textColor="?title_text_color"
android:id="@+id/song_title" android:id="@+id/song_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" /> android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="?title_text_color" />
<TextView <TextView
android:textColor="?caption_text_color"
android:id="@+id/song_info" android:id="@+id/song_info"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" /> android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?caption_text_color" />
</LinearLayout> </LinearLayout>
<ImageView <ImageView
android:id="@+id/menu" android:id="@+id/menu"
style="@style/OverFlowButton" style="@style/OverFlowButton"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginRight="2dp"
android:layout_marginEnd="2dp" android:layout_marginEnd="2dp"
android:layout_marginRight="2dp"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>