/* * 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.app.Activity; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.fastbootmobile.encore.app.PlaylistActivity; import com.fastbootmobile.encore.app.R; import com.fastbootmobile.encore.app.ui.AlbumArtImageView; import com.fastbootmobile.encore.app.ui.MaterialTransitionDrawable; import com.fastbootmobile.encore.framework.AutoPlaylistHelper; import com.fastbootmobile.encore.framework.PlaylistOrderer; import com.fastbootmobile.encore.framework.PluginsLookup; import com.fastbootmobile.encore.model.BoundEntity; import com.fastbootmobile.encore.model.Playlist; import com.fastbootmobile.encore.providers.ProviderConnection; import com.fastbootmobile.encore.utils.Utils; import com.fastbootmobile.encore.utils.ViewUtils; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange; import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder; import org.json.JSONException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Adapter to display a list of {@link com.fastbootmobile.encore.model.Playlist} in a RecyclerView */ public class PlaylistListAdapter extends RecyclerView.Adapter<PlaylistListAdapter.ViewHolder> implements DraggableItemAdapter<PlaylistListAdapter.ViewHolder> { private static final int VIEW_TYPE_HEADER = 1; private static final int VIEW_TYPE_REGULAR = 2; private List<Playlist> mPlaylists; private PlaylistOrderer mOrderer; private ItemDraggableRange mDragRange; static class PlaylistSort implements Comparator<Playlist> { private Map<String, Integer> mOrder; public PlaylistSort(Map<String, Integer> order) { mOrder = order; } public int compare(Playlist o1, Playlist o2) { int index1 = mOrder.containsKey(o1.getRef()) ? mOrder.get(o1.getRef()) : -1; int index2 = mOrder.containsKey(o2.getRef()) ? mOrder.get(o2.getRef()) : -1; return index1 - index2; } } /** * Default constructor */ public PlaylistListAdapter() { mPlaylists = new ArrayList<>(); // DraggableItemAdapter requires stable ID setHasStableIds(true); } private void ensureOrderer(Context context) { if (mOrderer == null && context != null) { mOrderer = new PlaylistOrderer(context); } else if (mOrderer == null) { throw new IllegalArgumentException("Orderer context is null and orderer is null too!"); } } /** * Sorts the list alphabetically */ public void sortList(Context context) throws JSONException { ensureOrderer(context); final Map<String, Integer> order = mOrderer.getOrder(); if (order != null) { Collections.sort(mPlaylists, new PlaylistSort(order)); } } /** * Adds the playlist to the adapter if it's not already there * * @param p The playlist to add */ public boolean addItemUnique(Playlist p) { if (!mPlaylists.contains(p)) { mPlaylists.add(p); return true; } else { return false; } } /** * Add all the elements into the adapter if they're not already there * * @param ps The collection of {@link com.fastbootmobile.encore.model.Playlist} to add */ public boolean addAllUnique(Collection<Playlist> ps) { boolean didChange = false; for (Playlist p : ps) { if (p != null && !mPlaylists.contains(p)) { mPlaylists.add(p); didChange = true; } } return didChange; } public void remove(String ref) { for (Playlist playlist : mPlaylists) { if (playlist.getRef().equals(ref)) { mPlaylists.remove(playlist); break; } } } public void clear() { mPlaylists.clear(); } /** * Returns whether or not the adapter contains the provided Playlist * * @param p The playlist to check * @return True if the adapter already has the playlist, false otherwise */ public boolean contains(Playlist p) { return mPlaylists.contains(p); } @Override public long getItemId(int position) { if (position == 0) { return -1; } else { return mPlaylists.get(position - 1).getRef().hashCode(); } } @Override public int getItemViewType(int position) { if (position == 0) { return VIEW_TYPE_HEADER; } else { return VIEW_TYPE_REGULAR; } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_REGULAR) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View root = inflater.inflate(R.layout.item_playlist_list, parent, false); ensureOrderer(parent.getContext()); return new ViewHolder(root); } else if (viewType == VIEW_TYPE_HEADER) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View root = inflater.inflate(R.layout.item_special_playlists, parent, false); ensureOrderer(parent.getContext()); return new SpecialViewHolder(root); } return null; // Should not happen } @Override public void onBindViewHolder(final ViewHolder holder, int position) { final int itemViewType = getItemViewType(position); if (itemViewType == VIEW_TYPE_HEADER) { SpecialViewHolder specialHolder = (SpecialViewHolder) holder; specialHolder.cardFavorites.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Context ctx = v.getContext(); Intent intent = PlaylistActivity.craftIntent(ctx, AutoPlaylistHelper.REF_SPECIAL_FAVORITES, null); ctx.startActivity(intent); } }); specialHolder.cardMostPlayed.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Context ctx = v.getContext(); Intent intent = PlaylistActivity.craftIntent(ctx, AutoPlaylistHelper.REF_SPECIAL_MOST_PLAYED, null); ctx.startActivity(intent); } }); } else if (itemViewType == VIEW_TYPE_REGULAR) { final Playlist item = mPlaylists.get(position - 1); holder.tvPlaylistName.setText(item.getName()); holder.ivCover.loadArtForPlaylist(item); if (item.isLoaded() || item.getSongsCount() > 0) { ProviderConnection provider = PluginsLookup.getDefault().getProvider(item.getProvider()); if (provider != null) { holder.tvPlaylistDesc.setText( String.format("%s / %s", holder.tvPlaylistDesc.getContext().getResources().getQuantityString(R.plurals.xx_songs, item.getSongsCount(), item.getSongsCount()), provider.getProviderName())); } holder.ivOfflineStatus.setVisibility(View.VISIBLE); switch (item.getOfflineStatus()) { case BoundEntity.OFFLINE_STATUS_NO: holder.ivOfflineStatus.setVisibility(View.GONE); break; case BoundEntity.OFFLINE_STATUS_DOWNLOADING: holder.ivOfflineStatus.setImageResource(R.drawable.ic_sync_in_progress); break; case BoundEntity.OFFLINE_STATUS_ERROR: holder.ivOfflineStatus.setImageResource(R.drawable.ic_sync_problem); break; case BoundEntity.OFFLINE_STATUS_PENDING: holder.ivOfflineStatus.setImageResource(R.drawable.ic_track_download_pending); break; case BoundEntity.OFFLINE_STATUS_READY: holder.ivOfflineStatus.setImageResource(R.drawable.ic_track_downloaded); break; } } else { holder.tvPlaylistDesc.setText(null); holder.ivOfflineStatus.setVisibility(View.GONE); } final int dragState = holder.getDragStateFlags(); if (((dragState & RecyclerViewDragDropManager.STATE_FLAG_IS_UPDATED) != 0)) { int bgColor = 0; if ((dragState & RecyclerViewDragDropManager.STATE_FLAG_IS_ACTIVE) != 0) { bgColor = 0xFFDDDDDD; } else if ((dragState & RecyclerViewDragDropManager.STATE_FLAG_DRAGGING) != 0) { bgColor = 0xFFAAAAAA; } if (bgColor != 0) { holder.container.setBackgroundColor(bgColor); } else { int[] attrs = new int[]{android.R.attr.selectableItemBackground /* index 0 */}; TypedArray ta = holder.container.getContext().obtainStyledAttributes(attrs); Drawable drawableFromTheme = ta.getDrawable(0 /* index */); ta.recycle(); holder.container.setBackground(drawableFromTheme); } } holder.container.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Context ctx = v.getContext(); Intent intent = PlaylistActivity.craftIntent(ctx, item, ((MaterialTransitionDrawable) holder.ivCover.getDrawable()).getFinalDrawable().getBitmap()); if (Utils.hasLollipop()) { ActivityOptions opt = ActivityOptions.makeSceneTransitionAnimation((Activity) ctx, holder.ivCover, "itemImage"); ctx.startActivity(intent, opt.toBundle()); } else { ctx.startActivity(intent); } } }); } } @Override public int getItemCount() { return mPlaylists.size() + 1; } @Override public boolean onCheckCanStartDrag(ViewHolder holder, int position, int x, int y) { // x, y --- relative from the itemView's top-left final View containerView = holder.container; final View dragHandleView = holder.ivDragHandle; if (containerView != null) { final int offsetX = containerView.getLeft() + (int) (ViewCompat.getTranslationX(containerView) + 0.5f); final int offsetY = containerView.getTop() + (int) (ViewCompat.getTranslationY(containerView) + 0.5f); return ViewUtils.hitTest(dragHandleView, x - offsetX, y - offsetY); } else { return false; } } @Override public ItemDraggableRange onGetItemDraggableRange(ViewHolder holder, int position) { if (mDragRange == null) { mDragRange = new ItemDraggableRange(1, getItemCount() - 1); } return mDragRange; } @Override public void onMoveItem(int fromPosition, int toPosition) { if (fromPosition == toPosition) { return; } Playlist p = mPlaylists.remove(fromPosition - 1); // Offset the header items mPlaylists.add(toPosition - 1, p); ensureOrderer(null); mOrderer.setOrder(mPlaylists); //notifyItemMoved(fromPosition - 1, toPosition - 1); } @Override public boolean onCheckCanDrop(int draggingPosition, int dropPosition) { return false; } /** * ViewHolder for items */ public static class ViewHolder extends AbstractDraggableItemViewHolder { public ViewGroup container; public View ivDragHandle; public AlbumArtImageView ivCover; public ImageView ivOfflineStatus; public TextView tvPlaylistName; public TextView tvPlaylistDesc; public ViewHolder(View v) { super(v); container = (ViewGroup) v.findViewById(R.id.container); ivDragHandle = v.findViewById(R.id.ivDragHandle); ivCover = (AlbumArtImageView) v.findViewById(R.id.ivCover); ivOfflineStatus = (ImageView) v.findViewById(R.id.ivOfflineStatus); tvPlaylistName = (TextView) v.findViewById(R.id.tvPlaylistName); tvPlaylistDesc = (TextView) v.findViewById(R.id.tvPlaylistDesc); } } public static class SpecialViewHolder extends ViewHolder { private TextView cardMostPlayed; private TextView cardFavorites; public SpecialViewHolder(View v) { super(v); cardMostPlayed = (TextView) v.findViewById(R.id.cardMostPlayed); cardFavorites = (TextView) v.findViewById(R.id.cardFavorites); } } }