Search is back. Now with recycler view and the ability to set a different xml file for song, album, artist and header results if needed. Also improved its appearance by adding elevation (inspired by Googles Inbox).

This commit is contained in:
Karim Abou Zeid 2015-04-25 12:17:41 +02:00
commit 428dd22296
14 changed files with 447 additions and 111 deletions

View file

@ -17,7 +17,6 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable; import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.signature.StringSignature; import com.bumptech.glide.signature.StringSignature;
@ -52,15 +51,6 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
return new ViewHolder(view); return new ViewHolder(view);
} }
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
Object tag = holder.albumArt.getTag();
if (tag instanceof Request) {
((Request) tag).clear();
}
}
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
final Album album = dataSet.get(position); final Album album = dataSet.get(position);
@ -69,31 +59,28 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
holder.title.setText(album.title); holder.title.setText(album.title);
holder.artist.setText(album.artistName); holder.artist.setText(album.artistName);
holder.albumArt.setTag( Glide.with(activity)
Glide.with(activity) .loadFromMediaStore(MusicUtil.getAlbumArtUri(album.id))
.loadFromMediaStore(MusicUtil.getAlbumArtUri(album.id)) .diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE) .signature(new StringSignature(album.albumArtPath))
.signature(new StringSignature(album.albumArtPath)) .error(R.drawable.default_album_art)
.error(R.drawable.default_album_art) .placeholder(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art) .listener(new RequestListener<Uri, GlideDrawable>() {
.listener(new RequestListener<Uri, GlideDrawable>() { @Override
@Override public boolean onException(Exception e, Uri model, Target<GlideDrawable> target, boolean isFirstResource) {
public boolean onException(Exception e, Uri model, Target<GlideDrawable> target, boolean isFirstResource) { if (usePalette)
if (usePalette) applyPalette(null, holder.title, holder.artist, holder.footer);
applyPalette(null, holder.title, holder.artist, holder.footer); return false;
return false; }
}
@Override @Override
public boolean onResourceReady(GlideDrawable resource, Uri model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { public boolean onResourceReady(GlideDrawable resource, Uri model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
if (usePalette) if (usePalette)
applyPalette(((GlideBitmapDrawable) resource).getBitmap(), holder.title, holder.artist, holder.footer); applyPalette(((GlideBitmapDrawable) resource).getBitmap(), holder.title, holder.artist, holder.footer);
return false; return false;
} }
}) })
.into(holder.albumArt) .into(holder.albumArt);
.getRequest()
);
} }
@Override @Override

View file

@ -11,7 +11,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.signature.StringSignature; import com.bumptech.glide.signature.StringSignature;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Album;
@ -47,28 +46,16 @@ public class ArtistAlbumAdapter extends RecyclerView.Adapter<ArtistAlbumAdapter.
return new ViewHolder(view); return new ViewHolder(view);
} }
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
Object tag = holder.albumArt.getTag();
if (tag instanceof Request) {
((Request) tag).clear();
}
}
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
final Album album = dataSet.get(position); final Album album = dataSet.get(position);
Glide.with(activity)
holder.albumArt.setTag(Glide.with(activity) .loadFromMediaStore(MusicUtil.getAlbumArtUri(album.id))
.loadFromMediaStore(MusicUtil.getAlbumArtUri(album.id)) .diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE) .signature(new StringSignature(album.albumArtPath))
.signature(new StringSignature(album.albumArtPath)) .error(R.drawable.default_album_art)
.error(R.drawable.default_album_art) .placeholder(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art) .into(holder.albumArt);
.into(holder.albumArt)
.getRequest()
);
holder.title.setText(album.title); holder.title.setText(album.title);
holder.year.setText(String.valueOf(album.year)); holder.year.setText(String.valueOf(album.year));

View file

@ -7,7 +7,6 @@ import android.view.LayoutInflater;
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.widget.ImageView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.TextView; import android.widget.TextView;
@ -56,12 +55,12 @@ public class PlaylistAdapter extends RecyclerView.Adapter<PlaylistAdapter.ViewHo
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final TextView playlistName; public final TextView playlistName;
private final ImageView menu; private final View menu;
public ViewHolder(View itemView) { public ViewHolder(View itemView) {
super(itemView); super(itemView);
playlistName = (TextView) itemView.findViewById(R.id.playlist_name); playlistName = (TextView) itemView.findViewById(R.id.playlist_name);
menu = (ImageView) itemView.findViewById(R.id.menu); menu = itemView.findViewById(R.id.menu);
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
menu.setOnClickListener(new View.OnClickListener() { menu.setOnClickListener(new View.OnClickListener() {
@Override @Override

View file

@ -1,39 +1,222 @@
package com.kabouzeid.gramophone.adapter; package com.kabouzeid.gramophone.adapter;
import android.app.Activity; import android.support.v4.util.Pair;
import android.support.v7.app.AppCompatActivity; 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.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.signature.StringSignature;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistThumbnailUrlLoader;
import com.kabouzeid.gramophone.loader.AlbumLoader;
import com.kabouzeid.gramophone.loader.ArtistLoader;
import com.kabouzeid.gramophone.loader.SongLoader;
import com.kabouzeid.gramophone.model.Album;
import com.kabouzeid.gramophone.model.Artist;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.NavigationUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
*/ */
public class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.ViewHolder> { public class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.ViewHolder> {
private Activity activity; private static final int HEADER = 0;
private static final int ALBUM = 1;
private static final int ARTIST = 2;
private static final int SONG = 3;
private AppCompatActivity activity;
private List results = Collections.emptyList();
public SearchAdapter(AppCompatActivity activity) { public SearchAdapter(AppCompatActivity activity) {
this.activity = activity; this.activity = activity;
} }
@Override public void search(String query) {
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { results = new ArrayList();
return null; if (!query.trim().equals("")) {
List songs = SongLoader.getSongs(activity, query);
if (!songs.isEmpty()) {
results.add(activity.getResources().getString(R.string.songs));
results.addAll(songs);
}
List artists = ArtistLoader.getArtists(activity, query);
if (!artists.isEmpty()) {
results.add(activity.getResources().getString(R.string.artists));
results.addAll(artists);
}
List albums = AlbumLoader.getAlbums(activity, query);
if (!albums.isEmpty()) {
results.add(activity.getResources().getString(R.string.albums));
results.addAll(albums);
}
}
if (results.isEmpty()) {
results.add(activity.getResources().getString(R.string.no_results).toUpperCase());
}
notifyDataSetChanged();
} }
@Override @Override
public void onBindViewHolder(ViewHolder holder, int position) { public int getItemViewType(int position) {
if (results.get(position) instanceof Album) return ALBUM;
if (results.get(position) instanceof Artist) return ARTIST;
if (results.get(position) instanceof Song) return SONG;
return HEADER;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ALBUM)
return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_search_album, parent, false), viewType);
if (viewType == ARTIST)
return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_search_artist, parent, false), viewType);
if (viewType == SONG)
return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_search_song, parent, false), viewType);
return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_search_header, parent, false), viewType);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ALBUM:
final Album album = (Album) results.get(position);
holder.title.setText(album.title);
holder.subTitle.setText(album.artistName);
Glide.with(activity)
.loadFromMediaStore(MusicUtil.getAlbumArtUri(album.id))
.error(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.signature(new StringSignature(album.albumArtPath))
.into(holder.image);
break;
case ARTIST:
final Artist artist = (Artist) results.get(position);
holder.title.setText(artist.name);
holder.subTitle.setText(MusicUtil.getArtistInfoString(activity, artist));
holder.image.setImageResource(R.drawable.default_artist_image);
LastFMArtistThumbnailUrlLoader.loadArtistThumbnailUrl(activity, artist.name, false, new LastFMArtistThumbnailUrlLoader.ArtistThumbnailUrlLoaderCallback() {
@Override
public void onArtistThumbnailUrlLoaded(final String url) {
Glide.with(activity)
.load(url)
.error(R.drawable.default_artist_image)
.placeholder(R.drawable.default_artist_image)
.into(holder.image);
}
});
break;
case SONG:
final Song song = (Song) results.get(position);
holder.title.setText(song.title);
holder.subTitle.setText(song.albumName);
break;
default:
holder.title.setText(results.get(position).toString());
break;
}
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return 0; return results.size();
} }
public class ViewHolder extends RecyclerView.ViewHolder { public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ViewHolder(View itemView) { private final ImageView image;
public final TextView title;
public final TextView subTitle;
private final int viewType;
public ViewHolder(View itemView, int viewType) {
super(itemView); super(itemView);
itemView.setOnClickListener(this);
this.viewType = viewType;
switch (viewType) {
case ALBUM:
image = (ImageView) itemView.findViewById(R.id.album_art);
title = (TextView) itemView.findViewById(R.id.album_title);
subTitle = (TextView) itemView.findViewById(R.id.album_artist);
break;
case ARTIST:
image = (ImageView) itemView.findViewById(R.id.artist_image);
title = (TextView) itemView.findViewById(R.id.artist_name);
subTitle = (TextView) itemView.findViewById(R.id.artist_info);
break;
case SONG:
image = null;
title = (TextView) itemView.findViewById(R.id.song_title);
subTitle = (TextView) itemView.findViewById(R.id.song_info);
itemView.findViewById(R.id.menu).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PopupMenu popupMenu = new PopupMenu(activity, view);
popupMenu.inflate(R.menu.menu_item_song);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return MenuItemClickHelper.handleSongMenuClick(activity, (Song) results.get(getAdapterPosition()), menuItem);
}
});
popupMenu.show();
}
});
break;
default:
image = null;
title = (TextView) itemView.findViewById(R.id.title);
subTitle = null;
itemView.setOnClickListener(null);
break;
}
}
@Override
public void onClick(View view) {
Object item = results.get(getAdapterPosition());
switch (viewType) {
case ALBUM:
NavigationUtil.goToAlbum(activity,
((Album) item).id,
new Pair[]{
Pair.create(image,
activity.getResources().getString(R.string.transition_album_cover)
)
});
break;
case ARTIST:
NavigationUtil.goToArtist(activity,
((Artist) item).id,
new Pair[]{
Pair.create(image,
activity.getResources().getString(R.string.transition_artist_image)
)
});
break;
case SONG:
ArrayList<Song> playList = new ArrayList<>();
playList.add((Song) item);
MusicPlayerRemote.openQueue(playList, 0, true);
break;
}
} }
} }
} }

View file

@ -13,7 +13,6 @@ import android.widget.TextView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.signature.StringSignature; import com.bumptech.glide.signature.StringSignature;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper; import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
@ -48,31 +47,19 @@ public class PlaylistSongAdapter extends RecyclerView.Adapter<PlaylistSongAdapte
return new ViewHolder(view); return new ViewHolder(view);
} }
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
Object tag = holder.albumArt.getTag();
if (tag instanceof Request) {
((Request) tag).clear();
}
}
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
final Song song = dataSet.get(position); final Song song = dataSet.get(position);
holder.songTitle.setText(song.title); holder.songTitle.setText(song.title);
holder.songInfo.setText(song.artistName); holder.songInfo.setText(song.artistName);
holder.albumArt.setTag( Glide.with(activity)
Glide.with(activity) .loadFromMediaStore(MusicUtil.getAlbumArtUri(song.albumId))
.loadFromMediaStore(MusicUtil.getAlbumArtUri(song.albumId)) .diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE) .signature(new StringSignature(String.valueOf(song.dateModified)))
.signature(new StringSignature(String.valueOf(song.dateModified))) .error(R.drawable.default_album_art)
.error(R.drawable.default_album_art) .placeholder(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art) .into(holder.albumArt);
.into(holder.albumArt)
.getRequest()
);
} }
@Override @Override

View file

@ -15,7 +15,6 @@ import android.widget.TextView;
import com.afollestad.materialdialogs.ThemeSingleton; import com.afollestad.materialdialogs.ThemeSingleton;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.signature.StringSignature; import com.bumptech.glide.signature.StringSignature;
import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.R;
@ -58,15 +57,6 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
return new ViewHolder(view); return new ViewHolder(view);
} }
@Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
Object tag = holder.albumArt.getTag();
if (tag instanceof Request) {
((Request) tag).clear();
}
}
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
return position == 0 ? SHUFFLE_BUTTON : SONG; return position == 0 ? SHUFFLE_BUTTON : SONG;
@ -79,16 +69,13 @@ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.ViewHolder> {
holder.songTitle.setText(song.title); holder.songTitle.setText(song.title);
holder.songInfo.setText(song.artistName); holder.songInfo.setText(song.artistName);
holder.albumArt.setTag( Glide.with(activity)
Glide.with(activity) .loadFromMediaStore(MusicUtil.getAlbumArtUri(song.albumId))
.loadFromMediaStore(MusicUtil.getAlbumArtUri(song.albumId)) .diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE) .signature(new StringSignature(String.valueOf(song.dateModified)))
.signature(new StringSignature(String.valueOf(song.dateModified))) .error(R.drawable.default_album_art)
.error(R.drawable.default_album_art) .placeholder(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art) .into(holder.albumArt);
.into(holder.albumArt)
.getRequest()
);
} else { } else {
holder.songTitle.setText(activity.getResources().getString(R.string.shuffle_all).toUpperCase()); holder.songTitle.setText(activity.getResources().getString(R.string.shuffle_all).toUpperCase());
holder.songTitle.setTextColor(ThemeSingleton.get().positiveColor); holder.songTitle.setTextColor(ThemeSingleton.get().positiveColor);

View file

@ -29,6 +29,7 @@ public class SearchActivity extends AbsBaseActivity {
public static final String TAG = SearchActivity.class.getSimpleName(); public static final String TAG = SearchActivity.class.getSimpleName();
private RecyclerView recyclerView; private RecyclerView recyclerView;
private SearchView searchView; private SearchView searchView;
private SearchAdapter searchAdapter;
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Override @Override
@ -40,7 +41,7 @@ public class SearchActivity extends AbsBaseActivity {
recyclerView = (RecyclerView) findViewById(R.id.recycler_view); recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setLayoutManager(new LinearLayoutManager(this));
SearchAdapter searchAdapter = new SearchAdapter(this); searchAdapter = new SearchAdapter(this);
recyclerView.setAdapter(searchAdapter); recyclerView.setAdapter(searchAdapter);
recyclerView.setOnTouchListener(new View.OnTouchListener() { recyclerView.setOnTouchListener(new View.OnTouchListener() {
@ -151,6 +152,7 @@ public class SearchActivity extends AbsBaseActivity {
} }
private void search(String query) { private void search(String query) {
if (searchAdapter != null)
searchAdapter.search(query);
} }
} }

View file

@ -2,7 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="72dp" android:layout_height="72dp"
android:background="?rect_selector"> 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"

View file

@ -3,7 +3,7 @@
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:background="?rect_selector"> android:foreground="?rect_selector">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="72dp"
android:background="?card_color"
android:elevation="2dp"
android:foreground="?rect_selector"
tools:ignore="UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<com.kabouzeid.gramophone.views.SquareImageView
android:id="@+id/album_art"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:gravity="center"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/album_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="?title_text_color" />
<TextView
android:id="@+id/album_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?caption_text_color" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/short_separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginLeft="72dp"
android:layout_marginStart="72dp"
android:background="?separator_color" />
</FrameLayout>

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="72dp"
android:background="?card_color"
android:elevation="2dp"
android:foreground="?rect_selector"
tools:ignore="UnusedAttribute">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/artist_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:gravity="center"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/artist_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="?title_text_color" />
<TextView
android:id="@+id/artist_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?caption_text_color" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/short_separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginLeft="72dp"
android:layout_marginStart="72dp"
android:background="?separator_color" />
</FrameLayout>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:textColor="?android:textColorSecondary"
android:id="@+id/title"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="72dp"
android:background="?card_color"
android:elevation="2dp"
android:foreground="?rect_selector"
tools:ignore="UnusedAttribute">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingStart="16dp"
tools:ignore="RtlSymmetry">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/song_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="?title_text_color" />
<TextView
android:id="@+id/song_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?caption_text_color" />
</LinearLayout>
<ImageView
android:id="@+id/menu"
style="@style/OverFlowButton"
android:layout_gravity="center_vertical"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp" />
</LinearLayout>
<View
android:id="@+id/separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="?separator_color" />
</FrameLayout>