package com.battlelancer.seriesguide.adapters; import android.content.Context; import android.os.Build; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.battlelancer.seriesguide.R; import com.battlelancer.seriesguide.settings.DisplaySettings; import com.battlelancer.seriesguide.thetvdbapi.TvdbImageTools; import com.battlelancer.seriesguide.util.ServiceUtils; import com.battlelancer.seriesguide.util.TimeTools; import com.battlelancer.seriesguide.util.Utils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Sectioned adapter displaying recently watched episodes, episodes released today and episodes * recently watched by trakt friends. */ public class NowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public static final String TRAKT_ACTION_WATCH = "watch"; public interface ItemClickListener { void onItemClick(View view, int position); } static class ReleasedViewHolder extends RecyclerView.ViewHolder { TextView show; TextView episode; TextView timestamp; TextView info; ImageView poster; public ReleasedViewHolder(View itemView, final ItemClickListener listener) { super(itemView); show = (TextView) itemView.findViewById(R.id.textViewReleasedShow); episode = (TextView) itemView.findViewById(R.id.textViewReleasedEpisode); timestamp = (TextView) itemView.findViewById(R.id.textViewReleasedTimestamp); info = (TextView) itemView.findViewById(R.id.textViewReleasedInfo); poster = (ImageView) itemView.findViewById(R.id.imageViewReleasedPoster); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION && listener != null) { listener.onItemClick(v, position); } } }); } } static class HistoryViewHolder extends RecyclerView.ViewHolder { public TextView show; public TextView episode; public TextView timestamp; public ImageView poster; public TextView username; public ImageView avatar; public ImageView type; public HistoryViewHolder(View itemView, final ItemClickListener listener) { super(itemView); show = (TextView) itemView.findViewById(R.id.textViewFriendShow); episode = (TextView) itemView.findViewById(R.id.textViewFriendEpisode); timestamp = (TextView) itemView.findViewById(R.id.textViewFriendTimestamp); poster = (ImageView) itemView.findViewById(R.id.imageViewFriendPoster); username = (TextView) itemView.findViewById(R.id.textViewFriendUsername); avatar = (ImageView) itemView.findViewById(R.id.imageViewFriendAvatar); type = (ImageView) itemView.findViewById(R.id.imageViewFriendActionType); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION && listener != null) { listener.onItemClick(v, position); } } }); } } static class MoreViewHolder extends RecyclerView.ViewHolder { public TextView title; public MoreViewHolder(View itemView, final ItemClickListener listener) { super(itemView); title = (TextView) itemView.findViewById(R.id.textViewNowMoreText); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION && listener != null) { listener.onItemClick(v, position); } } }); } } static class HeaderViewHolder extends RecyclerView.ViewHolder { public TextView title; public HeaderViewHolder(View itemView) { super(itemView); title = (TextView) itemView.findViewById(R.id.textViewGridHeader); } } @Retention(RetentionPolicy.SOURCE) @IntDef({ ItemType.RELEASED, ItemType.HISTORY, ItemType.FRIEND, ItemType.MORE_LINK, ItemType.HEADER }) public @interface ItemType { int RELEASED = 0; int HISTORY = 1; int FRIEND = 2; int MORE_LINK = 3; int HEADER = 4; } @Retention(RetentionPolicy.SOURCE) @IntDef({ ViewType.RELEASED, ViewType.HISTORY, ViewType.MORE_LINK, ViewType.HEADER }) public @interface ViewType { int RELEASED = 0; int HISTORY = 1; int MORE_LINK = 2; int HEADER = 3; } private final Context context; private final ItemClickListener listener; private final int resIdDrawableCheckin; private final int resIdDrawableWatched; private List<NowItem> dataset; private List<NowItem> recentlyWatched; private List<NowItem> releasedToday; private List<NowItem> friendsRecently; public static class NowItem { public Integer episodeTvdbId; public Integer showTvdbId; public Integer movieTmdbId; public long timestamp; public String title; public String description; public String network; public String tvdbPosterUrl; public String username; public String avatar; public String action; @ItemType public int type; public NowItem releasedToday(String network) { this.network = network; this.type = ItemType.RELEASED; return this; } public NowItem recentlyWatchedLocal() { this.type = ItemType.HISTORY; return this; } public NowItem recentlyWatchedTrakt(@Nullable String action) { this.action = action; this.type = ItemType.HISTORY; return this; } public NowItem friend(String username, String avatar, String action) { this.username = username; this.avatar = avatar; this.action = action; this.type = ItemType.FRIEND; return this; } public NowItem tvdbIds(Integer episodeTvdbId, Integer showTvdbId) { this.episodeTvdbId = episodeTvdbId; this.showTvdbId = showTvdbId; return this; } public NowItem tmdbId(Integer movieTmdbId) { this.movieTmdbId = movieTmdbId; return this; } public NowItem displayData(long timestamp, String title, String description, String tvdbPosterUrl) { this.timestamp = timestamp; this.title = title; this.description = description; this.tvdbPosterUrl = tvdbPosterUrl; return this; } public NowItem moreLink(String title) { this.type = ItemType.MORE_LINK; this.title = title; return this; } public NowItem header(String title) { this.type = ItemType.HEADER; this.title = title; return this; } } public NowAdapter(Context context, ItemClickListener listener) { this.context = context; this.listener = listener; this.dataset = new ArrayList<>(); this.resIdDrawableCheckin = Utils.resolveAttributeToResourceId(context.getTheme(), R.attr.drawableCheckin); this.resIdDrawableWatched = Utils.resolveAttributeToResourceId(context.getTheme(), R.attr.drawableWatch); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { if (viewType == ViewType.HEADER) { View v = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.item_grid_header, viewGroup, false); return new HeaderViewHolder(v); } else if (viewType == ViewType.MORE_LINK) { View v = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.item_now_more, viewGroup, false); return new MoreViewHolder(v, listener); } else if (viewType == ViewType.RELEASED) { View v = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.item_now_released, viewGroup, false); return new ReleasedViewHolder(v, listener); } else if (viewType == ViewType.HISTORY) { return getHistoryViewHolder(viewGroup, listener); } else { throw new IllegalArgumentException("Using unrecognized view type."); } } @NonNull protected RecyclerView.ViewHolder getHistoryViewHolder(ViewGroup viewGroup, ItemClickListener itemClickListener) { View v = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.item_now_history, viewGroup, false); return new HistoryViewHolder(v, itemClickListener); } @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { NowItem item = getItem(position); if (viewHolder instanceof HeaderViewHolder) { HeaderViewHolder holder = (HeaderViewHolder) viewHolder; holder.title.setText(item.title); } else if (viewHolder instanceof MoreViewHolder) { MoreViewHolder holder = (MoreViewHolder) viewHolder; holder.title.setText(item.title); } else if (viewHolder instanceof ReleasedViewHolder) { ReleasedViewHolder holder = (ReleasedViewHolder) viewHolder; holder.show.setText(item.title); holder.episode.setText(item.description); // timestamp Date actualRelease = TimeTools.applyUserOffset(getContext(), item.timestamp); boolean displayExactDate = DisplaySettings.isDisplayExactDate(getContext()); holder.timestamp.setText(displayExactDate ? TimeTools.formatToLocalDateShort(getContext(), actualRelease) : TimeTools.formatToLocalRelativeTime(getContext(), actualRelease)); // absolute time and network StringBuilder releaseInfo = new StringBuilder(); // "10:00 PM / Network", as left aligned, exactly mirrored from show list releaseInfo.append(TimeTools.formatToLocalTime(getContext(), actualRelease)); if (!TextUtils.isEmpty(item.network)) { releaseInfo.append(" / ").append(item.network); } holder.info.setText(releaseInfo); // is a TVDb or no poster TvdbImageTools.loadShowPosterResizeSmallCrop(getContext(), holder.poster, item.tvdbPosterUrl); // set unique transition names if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { holder.poster.setTransitionName("nowAdapterPoster_" + position); } } else if (viewHolder instanceof HistoryViewHolder) { HistoryViewHolder holder = (HistoryViewHolder) viewHolder; if (item.type == ItemType.HISTORY) { // user history entry holder.username.setVisibility(View.GONE); holder.avatar.setVisibility(View.GONE); // a TVDb or no poster TvdbImageTools.loadShowPosterResizeSmallCrop(getContext(), holder.poster, item.tvdbPosterUrl); } else { // friend history entry holder.username.setVisibility(View.VISIBLE); holder.avatar.setVisibility(View.VISIBLE); holder.username.setText(item.username); // a TVDb or no poster TvdbImageTools.loadShowPosterResizeSmallCrop(getContext(), holder.poster, item.tvdbPosterUrl); // trakt avatar ServiceUtils.loadWithPicasso(getContext(), item.avatar).into(holder.avatar); } holder.show.setText(item.title); holder.episode.setText(item.description); holder.timestamp.setText( TimeTools.formatToLocalRelativeTime(getContext(), new Date(item.timestamp))); // action type indicator (only if showing trakt history) if (TRAKT_ACTION_WATCH.equals(item.action)) { holder.type.setImageResource(resIdDrawableWatched); holder.type.setVisibility(View.VISIBLE); } else if (item.action != null) { // check-in, scrobble holder.type.setImageResource(resIdDrawableCheckin); holder.type.setVisibility(View.VISIBLE); } else { holder.type.setVisibility(View.GONE); } } } @Override public int getItemCount() { return dataset.size(); } @Override public int getItemViewType(int position) { int itemType = getItem(position).type; switch (itemType) { case ItemType.RELEASED: return ViewType.RELEASED; case ItemType.HISTORY: case ItemType.FRIEND: return ViewType.HISTORY; case ItemType.MORE_LINK: return ViewType.MORE_LINK; case ItemType.HEADER: return ViewType.HEADER; } return 0; } protected Context getContext() { return context; } protected int getResIdDrawableWatched() { return resIdDrawableWatched; } protected int getResIdDrawableCheckin() { return resIdDrawableCheckin; } public NowItem getItem(int position) { return dataset.get(position); } public void setReleasedTodayData(List<NowItem> items) { int oldCount = releasedToday == null ? 0 : releasedToday.size(); int newCount = items == null ? 0 : items.size(); releasedToday = items; reloadData(); notifyAboutChanges(0, oldCount, newCount); } public void setRecentlyWatched(List<NowItem> items) { int oldCount = recentlyWatched == null ? 0 : recentlyWatched.size(); int newCount = items == null ? 0 : items.size(); // items start after released today (if any) int startPosition = releasedToday == null ? 0 : releasedToday.size(); recentlyWatched = items; reloadData(); notifyAboutChanges(startPosition, oldCount, newCount); } public void setFriendsRecentlyWatched(List<NowItem> items) { int oldCount = friendsRecently == null ? 0 : friendsRecently.size(); int newCount = items == null ? 0 : items.size(); // items start after released today and recently watched (if any) int startPosition = (releasedToday == null ? 0 : releasedToday.size()) + (recentlyWatched == null ? 0 : recentlyWatched.size()); friendsRecently = items; reloadData(); notifyAboutChanges(startPosition, oldCount, newCount); } private void reloadData() { dataset.clear(); if (releasedToday != null) { dataset.addAll(releasedToday); } if (recentlyWatched != null) { dataset.addAll(recentlyWatched); } if (friendsRecently != null) { dataset.addAll(friendsRecently); } } private void notifyAboutChanges(int startPosition, int oldItemCount, int newItemCount) { if (newItemCount == 0 && oldItemCount == 0) { return; } if (newItemCount == oldItemCount) { // identical number of items notifyItemRangeChanged(startPosition, oldItemCount); } else if (newItemCount > oldItemCount) { // more items than before if (oldItemCount > 0) { notifyItemRangeChanged(startPosition, oldItemCount); } notifyItemRangeInserted(startPosition + oldItemCount, newItemCount - oldItemCount); } else { // less items than before if (newItemCount > 0) { notifyItemRangeChanged(startPosition, newItemCount); } notifyItemRangeRemoved(startPosition + newItemCount, oldItemCount - newItemCount); } } }