/* * Copyright (C) 2014 Fastboot Mobile, LLC. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program 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. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses>. */ package com.fastbootmobile.encore.app.adapters; import android.content.Context; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.fastbootmobile.encore.app.R; import com.fastbootmobile.encore.app.ui.AlbumArtImageView; import com.fastbootmobile.encore.framework.PlaybackProxy; import com.fastbootmobile.encore.model.Artist; import com.fastbootmobile.encore.model.BoundEntity; import com.fastbootmobile.encore.model.Song; import com.fastbootmobile.encore.providers.ProviderAggregator; import com.fastbootmobile.encore.utils.Utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Adapter allowing to display a list of songs in a ListView */ public class SongsListAdapter extends BaseAdapter { /** * ViewHolder for the list items */ public static class ViewHolder { public TextView tvTitle; public TextView tvArtist; public TextView tvDuration; public ImageView ivOverflow; public AlbumArtImageView ivAlbumArt; public ImageView ivOffline; public ViewGroup vRoot; public int position; public Song song; public View vCurrentIndicator; } protected List<Song> mSongs; private boolean mShowAlbumArt; private RotateAnimation mSyncRotateAnimation; private View.OnClickListener mOverflowClickListener = new View.OnClickListener() { @Override public void onClick(View v) { final ViewHolder tag = (ViewHolder) v.getTag(); final Context context = tag.vRoot.getContext(); Utils.showSongOverflow((FragmentActivity) context, tag.ivOverflow, tag.song, false); } }; private Comparator<Song> mComparator = new Comparator<Song>() { @Override public int compare(Song lhs, Song rhs) { if (lhs.isLoaded() && rhs.isLoaded()) { return lhs.getTitle().compareTo(rhs.getTitle()); } else { return lhs.getRef().compareTo(rhs.getRef()); } } }; /** * Default constructor * @param showAlbumArt Whether or not to show album art in front of each item */ public SongsListAdapter(boolean showAlbumArt) { mSongs = new ArrayList<>(); mShowAlbumArt = showAlbumArt; } /** * Clear the current displayed songs */ public void clear() { mSongs.clear(); } public List<Song> getItems() { return mSongs; } /** * Adds a song to the adapter * @param song The song to add */ public void put(Song song) { if (!mSongs.contains(song)) { mSongs.add(song); } } public void putAll(Collection<Song> songs) { for (Song song : songs) { put(song); } } /** * Sorts songs alphabetically */ public void sortAll() { Collections.sort(mSongs, mComparator); } /** * Returns whether or not this adapter contains the provided song * @param s The song to check * @return true if the adapter contains the song, false otherwise */ public boolean contains(Song s) { return mSongs.contains(s); } /** * {@inheritDoc} */ @Override public int getCount() { return mSongs.size(); } /** * {@inheritDoc} */ @Override public Song getItem(int i) { return mSongs.get(i); } /** * {@inheritDoc} */ @Override public long getItemId(int i) { return mSongs.get(i).getRef().hashCode(); } /** * {@inheritDoc} */ @Override public View getView(final int position, final View convertView, ViewGroup parent) { final Context ctx = parent.getContext(); assert ctx != null; final ProviderAggregator aggregator = ProviderAggregator.getDefault(); View root = convertView; if (convertView == null) { // Recycle the existing view LayoutInflater inflater = LayoutInflater.from(ctx); root = inflater.inflate(R.layout.item_playlist_view, parent, false); assert root != null; ViewHolder holder = new ViewHolder(); holder.tvTitle = (TextView) root.findViewById(R.id.tvTitle); holder.tvArtist = (TextView) root.findViewById(R.id.tvArtist); holder.tvDuration = (TextView) root.findViewById(R.id.tvDuration); holder.ivOverflow = (ImageView) root.findViewById(R.id.ivOverflow); holder.ivAlbumArt = (AlbumArtImageView) root.findViewById(R.id.ivAlbumArt); holder.ivOffline = (ImageView) root.findViewById(R.id.ivOffline); holder.vCurrentIndicator = root.findViewById(R.id.currentSongIndicator); holder.vRoot = (ViewGroup) root; holder.vRoot.setLayerType(View.LAYER_TYPE_HARDWARE, null); if (mShowAlbumArt) { // Fixup some style stuff holder.ivAlbumArt.setVisibility(View.VISIBLE); } else { holder.ivAlbumArt.setVisibility(View.GONE); } holder.ivOverflow.setOnClickListener(mOverflowClickListener); holder.ivOverflow.setTag(holder); root.setTag(holder); } final Song song = getItem(position); final ViewHolder tag = (ViewHolder) root.getTag(); // Update tag tag.position = position; tag.song = song; root.setTag(tag); // Fill fields if (song != null && song.isLoaded()) { tag.tvTitle.setText(song.getTitle()); tag.tvDuration.setText(Utils.formatTrackLength(song.getDuration())); if (mShowAlbumArt) { tag.ivAlbumArt.loadArtForSong(song); } if (song.getArtist() == null) { tag.tvArtist.setText(null); } else { Artist artist = aggregator.retrieveArtist(song.getArtist(), song.getProvider()); if (artist != null) { tag.tvArtist.setText(artist.getName()); } else { tag.tvArtist.setText("..."); } } } else { tag.tvTitle.setText("..."); tag.tvDuration.setText("..."); tag.tvArtist.setText("..."); if (mShowAlbumArt) { tag.ivAlbumArt.setDefaultArt(); } } // Set current song indicator tag.vCurrentIndicator.setVisibility(View.INVISIBLE); Song currentSong = PlaybackProxy.getCurrentTrack(); if (currentSong != null && currentSong.equals(tag.song)) { tag.vCurrentIndicator.setVisibility(View.VISIBLE); } if (song != null) { // Set alpha based on offline availability and mode if ((aggregator.isOfflineMode() && song.getOfflineStatus() != BoundEntity.OFFLINE_STATUS_READY) || !song.isAvailable()) { Utils.setChildrenAlpha(tag.vRoot, Float.parseFloat(ctx.getResources().getString(R.string.unavailable_track_alpha))); } else { Utils.setChildrenAlpha(tag.vRoot, 1.0f); } // Show offline indicator in any case tag.ivOffline.setVisibility(View.VISIBLE); tag.ivOffline.clearAnimation(); if (song.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_READY) { tag.ivOffline.setImageResource(R.drawable.ic_track_downloaded); } else if (song.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_DOWNLOADING) { tag.ivOffline.setImageResource(R.drawable.ic_sync_in_progress); if (mSyncRotateAnimation == null && tag.ivOffline.getMeasuredWidth() != 0) { mSyncRotateAnimation = new RotateAnimation(0, -360, tag.ivOffline.getMeasuredWidth() / 2.0f, tag.ivOffline.getMeasuredHeight() / 2.0f); mSyncRotateAnimation.setRepeatMode(Animation.INFINITE); mSyncRotateAnimation.setRepeatCount(Animation.INFINITE); mSyncRotateAnimation.setDuration(1000); mSyncRotateAnimation.setInterpolator(new LinearInterpolator()); } if (mSyncRotateAnimation != null) { tag.ivOffline.startAnimation(mSyncRotateAnimation); } } else if (song.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_ERROR) { tag.ivOffline.setImageResource(R.drawable.ic_sync_problem); } else if (song.getOfflineStatus() == BoundEntity.OFFLINE_STATUS_PENDING) { tag.ivOffline.setImageResource(R.drawable.ic_track_download_pending); } else { tag.ivOffline.setVisibility(View.GONE); } } return root; } }