/* * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.examples.abelanav2.ui.utils; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.BaseTarget; import com.bumptech.glide.request.target.SizeReadyCallback; import com.examples.abelanav2.R; import com.examples.abelanav2.grpcclient.AbelanaClient; import com.examples.abelanav2.grpcclient.PhotoInfo; import com.examples.abelanav2.ui.PicturesFragment; import java.io.IOException; import java.util.Date; import java.util.List; /** * This is the adapter used to put the photos in the recycler view cards. */ public class PhotoAdapter extends RecyclerView.Adapter { /** * The list of photo to display. */ private List<PhotoInfo> mPhotoList; /** * The position of the selected item in the list where the contextual * menu was called. */ private int mPosition; /** * The fragment type. */ private AbelanaClient.PhotoListType mFragmentType; /** * The fragment. */ private PicturesFragment mFragment; /** * A listener for events in the Photo cards. */ private PhotoAdapterListener mListener; /** * The type of ViewHolder for a progress indicator. */ private static final int VIEW_PROGRESS = 0; /** * The type of ViewHolder for a Photo. */ private static final int VIEW_PHOTO = 1; /** * The minimum amount of items to have below your current scroll position * before loading more. */ private int mVisibleThreshold = 2; /** * The last visible item in the recyclerview and the total number of items. */ private int mLastVisibleItem, mTotalItemCount; /** * Are we already loading images. */ private boolean mLoading; /** * A listener to inform that we need to fetch more photos. */ private OnLoadMoreListener mOnLoadMoreListener; /** * Constructor. * @param pPhotoList the initial list of photos. * @param pFragmentType the type of fragment using the adapter. * @param pPhotoAdapterListener the listener for events on the photo cards. * @param recyclerView the RecyclerView. * @param fragment the fragment using the adapter. */ public PhotoAdapter(List<PhotoInfo> pPhotoList, AbelanaClient.PhotoListType pFragmentType, PhotoAdapterListener pPhotoAdapterListener, RecyclerView recyclerView, PicturesFragment fragment) { this.mPhotoList = pPhotoList; this.mFragmentType = pFragmentType; this.mListener = pPhotoAdapterListener; this.mFragment = fragment; if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) { final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); recyclerView.setOnScrollListener( new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); mTotalItemCount = linearLayoutManager.getItemCount(); mLastVisibleItem = linearLayoutManager .findLastVisibleItemPosition(); if (!mLoading && mTotalItemCount <= (mLastVisibleItem + mVisibleThreshold)) { // End has been reached // Do something if (mOnLoadMoreListener != null) { mOnLoadMoreListener.onLoadMore(); } mLoading = true; } } }); } } /** * Returns the photo list. * @return the photo list. */ public List<PhotoInfo> getPhotoList() { return mPhotoList; } /** * Returns the position of the item in the list where the contextual * menu was called. * @return the item position. */ public int getPosition() { return mPosition; } /** * When the contextual menu is called, sets the position of the item in * the list. * @param position the item position. */ public void setPosition(int position) { this.mPosition = position; } @Override public int getItemViewType(int position) { if (mPhotoList.get(position) != null) return VIEW_PHOTO; else return VIEW_PROGRESS; } @Override public int getItemCount() { return mPhotoList.size(); } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, int i) { if (holder instanceof PhotoViewHolder) { final PhotoViewHolder photoViewHolder = (PhotoViewHolder) holder; PhotoInfo pi = mPhotoList.get(i); photoViewHolder.vDate.setText(getDate(pi.date)); photoViewHolder.vDescription.setText(pi.description); if (mFragmentType == AbelanaClient.PhotoListType.PHOTO_LIST_STREAM || mFragmentType == AbelanaClient .PhotoListType.PHOTO_LIST_LIKES) { if (pi.vote == 1) { photoViewHolder.vVoteThumbsUp.setSelected(true); photoViewHolder.vVoteThumbsDown.setSelected(false); } if (pi.vote == -1) { photoViewHolder.vVoteThumbsDown.setSelected(true); photoViewHolder.vVoteThumbsUp.setSelected(false); } if (pi.vote == 0) { photoViewHolder.vVoteThumbsUp.setSelected(false); photoViewHolder.vVoteThumbsDown.setSelected(false); } photoViewHolder.vEdit.setVisibility(View.GONE); photoViewHolder.vDelete.setVisibility(View.GONE); photoViewHolder.vVoteThumbsUp.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setPosition(photoViewHolder.getPosition()); mListener.onPhotoCardClick(v, getPosition()); } }); photoViewHolder.vVoteThumbsDown.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setPosition(photoViewHolder.getPosition()); mListener.onPhotoCardClick(v, getPosition()); } }); } if (mFragmentType == AbelanaClient.PhotoListType.PHOTO_LIST_MINE) { photoViewHolder.vVoteThumbsUp.setVisibility(View.GONE); photoViewHolder.vVoteThumbsDown.setVisibility(View.GONE); photoViewHolder.vEdit.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setPosition(photoViewHolder.getPosition()); mListener.onPhotoCardClick(v, getPosition()); } }); photoViewHolder.vDelete.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setPosition(photoViewHolder.getPosition()); mListener.onPhotoCardClick(v, getPosition()); } }); } photoViewHolder.vSetWallpaper.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { setPosition(photoViewHolder.getPosition()); mListener.onPhotoCardClick(v, getPosition()); } }); photoViewHolder.itemView.setOnLongClickListener( new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { setPosition(photoViewHolder.getPosition()); return false; } }); Glide.with(mFragment) .load(pi.url) .crossFade() .into(photoViewHolder.vPhoto); } else { ((ProgressViewHolder) holder).progressBar.setIndeterminate(true); } } /** * Indicates that we are no longer fetching data. */ public void setLoaded() { mLoading = false; } /** * Interface used to communicate back with the fragment/activity that we * need to get more data for the RecyclerView. */ public interface OnLoadMoreListener { /** * Called when we need to get more data. */ void onLoadMore(); } /** * Adds a LoadMoreListener to this adapter (will be called when we need * more data). * @param onLoadMoreListener the listener. */ public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) { this.mOnLoadMoreListener = onLoadMoreListener; } /** * Interface used to communicate when an even happens on a picture. */ public interface PhotoAdapterListener { /** * Called when a button on a photo card is clicked. * @param v the view clicked. * @param position the position in the list of photos. */ void onPhotoCardClick(View v, int position); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { RecyclerView.ViewHolder viewHolder; if (viewType == VIEW_PHOTO) { View photoView = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.photo_card, viewGroup, false); viewHolder = new PhotoViewHolder(photoView); } else { View progressBarView = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.progress_item, viewGroup, false); viewHolder = new ProgressViewHolder(progressBarView); } return viewHolder; } @Override public void onViewRecycled(RecyclerView.ViewHolder viewHolder) { viewHolder.itemView.setOnLongClickListener(null); super.onViewRecycled(viewHolder); } /** * Returns the date as a String. * @param time timestamp in microseconds. * @return a String formatted date. */ private String getDate(long time) { return android.text.format.DateFormat.getDateFormat(mFragment .getActivity().getApplicationContext()) .format(new Date(time / 1000)); } /** * A ViewHolder that holds all the useful UI elements to display a picture. */ public class PhotoViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener { /** * The photo ImageView. */ public ImageView vPhoto; /** * The date TextView. */ public TextView vDate; /** * The Description TextView. */ public TextView vDescription; /** * The ThumbsUp button. */ public ImageButton vVoteThumbsUp; /** * The ThumbsDown button. */ public ImageButton vVoteThumbsDown; /** * The edit button. */ public ImageButton vSetWallpaper; /** * The edit button. */ public ImageButton vEdit; /** * The edit button. */ public ImageButton vDelete; /** * Creates a ViewHolder from a View. * @param v the view. */ public PhotoViewHolder(View v) { super(v); vPhoto = (ImageView) v.findViewById(R.id.imageViewPhoto); vDate = (TextView) v.findViewById(R.id.textViewDate); vDescription = (TextView) v.findViewById(R.id .textViewDescription); vVoteThumbsUp = (ImageButton) v.findViewById(R.id .imageButtonThumbsUp); vVoteThumbsDown = (ImageButton) v.findViewById(R.id .imageButtonThumbsDown); vSetWallpaper = (ImageButton) v.findViewById(R.id .imageButtonWallpaper); vEdit = (ImageButton) v.findViewById(R.id.imageButtonEdit); vDelete = (ImageButton) v.findViewById(R.id.imageButtonDelete); v.setOnCreateContextMenuListener(this); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { menu.add(Menu.NONE, R.id.context_menu_report, Menu.NONE, R.string .context_menu_report); } } /** * A ViewHolder that contains a progressbar. */ public class ProgressViewHolder extends RecyclerView.ViewHolder { /** * The progress bar. */ public ProgressBar progressBar; /** * Constructor. * @param v the view. */ public ProgressViewHolder(View v) { super(v); progressBar = (ProgressBar) v.findViewById(R.id.progressBar); } } }