diff --git a/.idea/modules.xml b/.idea/modules.xml index 4af7707..fad13b5 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,9 @@ - - + + + diff --git a/app/build.gradle b/app/build.gradle index 6b992cf..dd7147e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { compileSdkVersion 25 @@ -33,4 +34,10 @@ dependencies { compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.squareup.okhttp3:logging-interceptor:3.3.1' + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + compile 'com.android.support:design:25.4.0' + +} +repositories { + mavenCentral() } diff --git a/app/src/main/java/com/kotlinUtils/PaginatioinAdapter.kt b/app/src/main/java/com/kotlinUtils/PaginatioinAdapter.kt new file mode 100644 index 0000000..d2d363e --- /dev/null +++ b/app/src/main/java/com/kotlinUtils/PaginatioinAdapter.kt @@ -0,0 +1,391 @@ +package com.kotlinUtils + +import android.content.Context +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.bumptech.glide.DrawableRequestBuilder +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.drawable.GlideDrawable +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import com.suleiman.pagination.R +import com.suleiman.pagination.models.Result +import com.suleiman.pagination.utils.PaginationAdapterCallback +import java.util.ArrayList +import android.content.ClipData.Item + + + +/** + * Created by heinhtet on 12/9/2017. + */ + + +class PaginationAdapter(private val context: Context) : RecyclerView.Adapter() { + + private var movieResults: MutableList? = null + + private var isLoadingAdded = false + private var retryPageLoad = false + + private val mCallback: PaginationAdapterCallback + + private var errorMsg: String? = null + + var movies: MutableList? + get() = movieResults + set(movieResults) { + this.movieResults = movieResults + } + + val isEmpty: Boolean + get() = itemCount == 0 + + init { + this.mCallback = context as PaginationAdapterCallback + movieResults = ArrayList() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder? { + var viewHolder: RecyclerView.ViewHolder? = null + val inflater = LayoutInflater.from(parent.context) + when (viewType) { + ITEM -> { + val viewItem = inflater.inflate(R.layout.item_list, parent, false) + viewHolder = MovieVH(viewItem) + } + LOADING -> { + val viewLoading = inflater.inflate(R.layout.item_progress, parent, false) + viewHolder = LoadingVH(viewLoading) + } + HERO -> { + val viewHero = inflater.inflate(R.layout.item_hero, parent, false) + viewHolder = HeroVH(viewHero) + } + } + return viewHolder + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val result = movieResults!![position] // Movie + + when (getItemViewType(position)) { + HERO -> { + val heroVh = holder as HeroVH + heroVh.mMovieTitle.text = result.title + if (result.title != null) { + heroVh.mYear.text = formatYearLabel(result) + } else { + heroVh.mYear.text = "null" + } + heroVh.mMovieDesc.text = result.overview + loadImage(result.backdropPath) + .into(heroVh.mPosterImg) + } + ITEM -> { + val movieVH = holder as MovieVH + movieVH.mMovieTitle.text = result.title + if (result.title != null) { + movieVH.mYear.text = formatYearLabel(result) + } + movieVH.mMovieDesc.text = result.overview + // load movie thumbnail + loadImage(result.posterPath) + .listener(object : RequestListener { + override fun onException(e: Exception, model: String, target: Target, isFirstResource: Boolean): Boolean { + // TODO: 08/11/16 handle failure + movieVH.mProgress.visibility = View.GONE + return false + } + + override fun onResourceReady(resource: GlideDrawable, model: String, target: Target, isFromMemoryCache: Boolean, isFirstResource: Boolean): Boolean { + // image ready, hide progress now + movieVH.mProgress.visibility = View.GONE + return false // return false if you want Glide to handle everything else. + } + }).into(movieVH.mPosterImg) + } + + LOADING -> { + val loadingVH = holder as LoadingVH + + if (retryPageLoad) { + loadingVH.mErrorLayout.visibility = View.VISIBLE + loadingVH.mProgressBar.visibility = View.GONE + + loadingVH.mErrorTxt.text = if (errorMsg != null) + errorMsg + else + context.getString(R.string.error_msg_unknown) + + } else { + loadingVH.mErrorLayout.visibility = View.GONE + loadingVH.mProgressBar.visibility = View.VISIBLE + } + } + } + } + + override fun getItemCount(): Int { + if (movieResults == null) { + mCallback.emptyLayout() + return 0 + } else { + return movieResults!!.size + } + // return movieResults == null ? 0 : movieResults.size() + } + + override fun getItemViewType(position: Int): Int { + return if (position % 3 == 0) { + HERO + } else { + if (position == movieResults!!.size - 1 && isLoadingAdded) LOADING else ITEM + + } + // if (position == 0) { + // return HERO; + // } else { + // return (position == movieResults.size() - 1 && isLoadingAdded) ? LOADING : ITEM; + // } + } + + /* + Helpers - bind Views + _________________________________________________________________________________________________ + */ + + /** + * @param result + * @return [releasedate] | [2letterlangcode] + */ + private fun formatYearLabel(result: Result?): String { + return if (result != null) { + (result.releaseDate.substring(0, 4) // we want the year only + + + " | " + + result.originalLanguage.toUpperCase()) + + } else { + "" + } + } + + /** + * Using Glide to handle image loading. + * Learn more about Glide here: + * [](http://blog.grafixartist.com/image-gallery-app-android-studio-1-4-glide/) + * + * @param posterPath from [Result.getPosterPath] + * @return Glide builder + */ + var imageUrl: String? = null + + private fun loadImage(posterPath: String?): DrawableRequestBuilder { + + if (posterPath != null) { + if (posterPath.contains("profile_image") || posterPath.contains("scontent")) { + imageUrl = posterPath + } else { + imageUrl = BASE_URL_IMG + posterPath + } + } else { + imageUrl = BASE_URL_IMG + posterPath + } + + return Glide + .with(context) + .load(imageUrl) + .diskCacheStrategy(DiskCacheStrategy.ALL) // cache both original & resized image + .centerCrop() + .crossFade() + } + + /* + Helpers - Pagination + _________________________________________________________________________________________________ + */ + + fun add(r: Result) { + movieResults!!.add(r) + notifyItemInserted(movieResults!!.size - 1) + } + + /*add items all*/ + fun addAll(moveResults: List) { + for (result in moveResults) { + add(result) + } + } + + /* remove items*/ + fun remove(r: Result?) { + val position = movieResults!!.indexOf(r) + if (position > -1) { + movieResults!!.removeAt(position) + notifyItemRemoved(position) + } + } + + /* remove all items*/ + fun clear() { + isLoadingAdded = false + while (itemCount > 0) { + remove(getItem(0)) + } + } + + /*remove specific row*/ + fun removeAtItemsPosition(position: Int) { + movieResults!!.removeAt(position) + notifyItemRemoved(position) + notifyItemRangeChanged(position, movieResults!!.size) + } + + /*update row */ + fun updateItemsAtPosition(position: Int, result: Result) { + movieResults!!.removeAt(position) + notifyItemChanged(position) + movieResults!!.add(position, result) + notifyItemChanged(position, movieResults!!.size) + + } + + /*add new row*/ + fun addRow(result: Result) { + movieResults!!.add(0, result) + notifyItemRangeChanged(0, movieResults!!.size) + } + + fun restoreItem(item: Result, position: Int) { + movieResults!!.add(position, item) + // notify item added by position + notifyItemInserted(position) + } + + /* add loading footer */ + fun addLoadingFooter() { + isLoadingAdded = true + add(Result()) + } + + /* remove loading footer view*/ + fun removeLoadingFooter() { + isLoadingAdded = false + val position = movieResults!!.size - 1 + val result = getItem(position) + + if (result != null) { + movieResults!!.removeAt(position) + notifyItemRemoved(position) + } + } + + fun getItem(position: Int): Result? { + return movieResults!![position] + } + + /** + * Displays Pagination retry footer view along with appropriate errorMsg + * + * @param show + * @param errorMsg to display if page load fails + */ + fun showRetry(show: Boolean, errorMsg: String?) { + retryPageLoad = show + notifyItemChanged(movieResults!!.size - 1) + + if (errorMsg != null) this.errorMsg = errorMsg + } + + + /* + View Holders + _________________________________________________________________________________________________ + */ + + /** + * Header ViewHolder + */ + public inner class HeroVH(itemView: View) : RecyclerView.ViewHolder(itemView) { + val mMovieTitle: TextView + val mMovieDesc: TextView + val mYear: TextView // displays "year | language" + val mPosterImg: ImageView + + init { + mMovieTitle = itemView.findViewById(R.id.movie_title) as TextView + mMovieDesc = itemView.findViewById(R.id.movie_desc) as TextView + mYear = itemView.findViewById(R.id.movie_year) as TextView + mPosterImg = itemView.findViewById(R.id.movie_poster) as ImageView + itemView.setOnClickListener { mCallback.onItemsClickListener(movieResults!![adapterPosition], adapterPosition) } + } + } + + override fun getItemId(position: Int): Long { + return movieResults!![position].id.toLong() + } + + /** + * Main list's content ViewHolder + */ + protected inner class MovieVH(itemView: View) : RecyclerView.ViewHolder(itemView) { + val mMovieTitle: TextView + val mMovieDesc: TextView + val mYear: TextView // displays "year | language" + val mPosterImg: ImageView + val mProgress: ProgressBar + + init { + + mMovieTitle = itemView.findViewById(R.id.movie_title) as TextView + mMovieDesc = itemView.findViewById(R.id.movie_desc) as TextView + mYear = itemView.findViewById(R.id.movie_year) as TextView + mPosterImg = itemView.findViewById(R.id.movie_poster) as ImageView + mProgress = itemView.findViewById(R.id.movie_progress) as ProgressBar + itemView.setOnClickListener { mCallback.onItemsClickListener(movieResults!![adapterPosition], adapterPosition) } + } + } + + + protected inner class LoadingVH(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + val mProgressBar: ProgressBar + val mRetryBtn: ImageButton + val mErrorTxt: TextView + val mErrorLayout: LinearLayout + + init { + + mProgressBar = itemView.findViewById(R.id.loadmore_progress) as ProgressBar + mRetryBtn = itemView.findViewById(R.id.loadmore_retry) as ImageButton + mErrorTxt = itemView.findViewById(R.id.loadmore_errortxt) as TextView + mErrorLayout = itemView.findViewById(R.id.loadmore_errorlayout) as LinearLayout + + mRetryBtn.setOnClickListener(this) + mErrorLayout.setOnClickListener(this) + } + + override fun onClick(view: View) { + when (view.id) { + R.id.loadmore_retry, R.id.loadmore_errorlayout -> { + showRetry(false, null) + mCallback.retryPageLoad() + } + } + } + } + + companion object { + // View Types + val ITEM = 0 + val LOADING = 1 + val HERO = 2 + + private val BASE_URL_IMG = "https://image.tmdb.org/t/p/w150" + } + +} diff --git a/app/src/main/java/com/kotlinUtils/PaginationScrollListener.kt b/app/src/main/java/com/kotlinUtils/PaginationScrollListener.kt new file mode 100644 index 0000000..67e8736 --- /dev/null +++ b/app/src/main/java/com/kotlinUtils/PaginationScrollListener.kt @@ -0,0 +1,42 @@ +package com.kotlinUtils + +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.util.Log + +/** + * Created by heinhtet on 12/9/2017. + */ + +abstract class PaginationScrollListener(manager: LinearLayoutManager) : RecyclerView.OnScrollListener() { + + var layoutManager: LinearLayoutManager = manager + + override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val visibleItemCount = layoutManager.childCount + val totalItemCount = layoutManager.itemCount + val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() + Log.i(" scroll change ", "visible count " + visibleItemCount) + Log.i(" scroll change ", "total items count " + totalItemCount) + Log.i(" scroll change ", "first visible items count " + firstVisibleItemPosition) + + if (!isLoading() && !isLastPage()) { + if (visibleItemCount + firstVisibleItemPosition >= totalItemCount && firstVisibleItemPosition >= 0) { + loadMoreItems() + } + } + + + } + + protected abstract fun loadMoreItems() + + abstract fun getTotalPageCount(): Int + + abstract fun isLastPage(): Boolean + + abstract fun isLoading(): Boolean + +} \ No newline at end of file diff --git a/app/src/main/java/com/kotlinUtils/RecyclerItemTouchHelper.kt b/app/src/main/java/com/kotlinUtils/RecyclerItemTouchHelper.kt new file mode 100644 index 0000000..12e0001 --- /dev/null +++ b/app/src/main/java/com/kotlinUtils/RecyclerItemTouchHelper.kt @@ -0,0 +1,66 @@ +package com.kotlinUtils + +import android.graphics.Canvas +import android.support.v7.widget.helper.ItemTouchHelper +import android.support.v7.widget.RecyclerView + + +/** + * Created by heinhtet on 12/10/2017. + */ + + + class RecyclerItemTouchHelper(dragDirs: Int, swipeDirs: Int, HelperListener: RecyclerItemTouchHelperListener) : + ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) { + private var listener: RecyclerItemTouchHelperListener? = null + + init { + listener = HelperListener + } + + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + return true + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + if (viewHolder != null) { + val foregroundView = (viewHolder as PaginationAdapter.HeroVH).itemView + ItemTouchHelper.Callback.getDefaultUIUtil().onSelected(foregroundView) + } + } + + override fun onChildDrawOver(c: Canvas, recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, + actionState: Int, isCurrentlyActive: Boolean) { + var foregroundView = (viewHolder as PaginationAdapter.HeroVH).itemView + ItemTouchHelper.Callback.getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY, + actionState, isCurrentlyActive) + } + + override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder) { + val foregroundView = (viewHolder as PaginationAdapter.HeroVH).itemView + ItemTouchHelper.Callback.getDefaultUIUtil().clearView(foregroundView) + } + + override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, + actionState: Int, isCurrentlyActive: Boolean) { + val foregroundView = (viewHolder as PaginationAdapter.HeroVH).itemView + + ItemTouchHelper.Callback.getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY, + actionState, isCurrentlyActive) + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + listener!!.onSwiped(viewHolder, direction, viewHolder.adapterPosition) + } + + override fun convertToAbsoluteDirection(flags: Int, layoutDirection: Int): Int { + return super.convertToAbsoluteDirection(flags, layoutDirection) + } + + interface RecyclerItemTouchHelperListener { + fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int, position: Int) + } +} + diff --git a/app/src/main/java/com/suleiman/pagination/MainActivity.java b/app/src/main/java/com/suleiman/pagination/MainActivity.java index 656e8ad..0f9a9f0 100644 --- a/app/src/main/java/com/suleiman/pagination/MainActivity.java +++ b/app/src/main/java/com/suleiman/pagination/MainActivity.java @@ -1,19 +1,30 @@ package com.suleiman.pagination; +import android.content.ClipData; import android.content.Context; +import android.graphics.Color; import android.net.ConnectivityManager; import android.os.Bundle; +import android.support.design.widget.Snackbar; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; +import com.kotlinUtils.RecyclerItemTouchHelper; import com.suleiman.pagination.api.MovieApi; import com.suleiman.pagination.api.MovieService; import com.suleiman.pagination.models.Result; @@ -21,6 +32,9 @@ import com.suleiman.pagination.utils.PaginationAdapterCallback; import com.suleiman.pagination.utils.PaginationScrollListener; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -28,19 +42,20 @@ import retrofit2.Callback; import retrofit2.Response; -public class MainActivity extends AppCompatActivity implements PaginationAdapterCallback { +public class MainActivity extends AppCompatActivity implements PaginationAdapterCallback, SwipeRefreshLayout.OnRefreshListener { private static final String TAG = "MainActivity"; - PaginationAdapter adapter; + com.kotlinUtils.PaginationAdapter adapter; LinearLayoutManager linearLayoutManager; - + GridLayoutManager gridLayoutManager; + List movieList; RecyclerView rv; ProgressBar progressBar; LinearLayout errorLayout; Button btnRetry; TextView txtError; - + SwipeRefreshLayout swipeRefreshLayout; private static final int PAGE_START = 1; private boolean isLoading = false; @@ -56,28 +71,48 @@ public class MainActivity extends AppCompatActivity implements PaginationAdapter protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - + movieList = new ArrayList<>(); rv = (RecyclerView) findViewById(R.id.main_recycler); progressBar = (ProgressBar) findViewById(R.id.main_progress); errorLayout = (LinearLayout) findViewById(R.id.error_layout); btnRetry = (Button) findViewById(R.id.error_btn_retry); txtError = (TextView) findViewById(R.id.error_txt_cause); - - adapter = new PaginationAdapter(this); + swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_layout); + swipeRefreshLayout.setColorSchemeColors(Color.BLACK, Color.BLUE, Color.YELLOW, Color.GREEN); + swipeRefreshLayout.setOnRefreshListener(this); + adapter = new com.kotlinUtils.PaginationAdapter(this); linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); - rv.setLayoutManager(linearLayoutManager); - rv.setItemAnimator(new DefaultItemAnimator()); + gridLayoutManager = new GridLayoutManager(this, 2 + ); + gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + switch (adapter.getItemViewType(position)) { + case PaginationAdapter.HERO: + return 2; + case PaginationAdapter.ITEM: + return 1; + case PaginationAdapter.LOADING: + return 2; + default: + return -1; + } + } + }); + rv.setLayoutManager(gridLayoutManager); + rv.setItemAnimator(new DefaultItemAnimator()); rv.setAdapter(adapter); - rv.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) { + + rv.addOnScrollListener(new com.kotlinUtils.PaginationScrollListener(gridLayoutManager) { @Override protected void loadMoreItems() { + Log.d("load more ", "call back"); isLoading = true; currentPage += 1; - - loadNextPage(); + loadApi("next"); } @Override @@ -98,13 +133,11 @@ public boolean isLoading() { //init service and load data movieService = MovieApi.getClient().create(MovieService.class); - - loadFirstPage(); - + loadApi("first"); btnRetry.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - loadFirstPage(); + loadApi("first"); } }); @@ -128,8 +161,14 @@ public void onResponse(Call call, Response respo progressBar.setVisibility(View.GONE); adapter.addAll(results); - if (currentPage <= TOTAL_PAGES) adapter.addLoadingFooter(); - else isLastPage = true; + if (currentPage <= TOTAL_PAGES) { + Log.d("load need", "more"); + isLoading = true; + adapter.addLoadingFooter(); + } else { + isLastPage = true; + } + } @Override @@ -149,7 +188,10 @@ private List fetchResults(Response response) { return topRatedMovies.getResults(); } - private void loadNextPage() { + private void loadNextPage(String type) { + if (type.equals("first")) { + hideErrorView(); + } Log.d(TAG, "loadNextPage: " + currentPage); callTopRatedMoviesApi().enqueue(new Callback() { @@ -173,6 +215,48 @@ public void onFailure(Call call, Throwable t) { }); } + /*adding type and call api method write once*/ + private void loadApi(final String type) { + + if (type.equals("first")) { + hideErrorView(); + } + + callTopRatedMoviesApi().enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (type.equals("first")) { + hideSwipeRefresh(); + Log.d(TAG, "loadFirstPage: " + currentPage); + hideErrorView(); + List results = fetchResults(response); + progressBar.setVisibility(View.GONE); + adapter.addAll(results); + } else { + Log.d(TAG, "loadNextPage: " + currentPage); + adapter.removeLoadingFooter(); + isLoading = false; + List results = fetchResults(response); + adapter.addAll(results); + } + if (currentPage != TOTAL_PAGES) { + Log.d("load need", ""); + adapter.addLoadingFooter(); + } else isLastPage = true; + } + + @Override + public void onFailure(Call call, Throwable t) { + t.printStackTrace(); + if (type.equals("first")) { + showErrorView(t); + } else { + adapter.showRetry(true, fetchErrorMessage(t)); + } + } + }); + } + /** * Performs a Retrofit call to the top rated movies API. @@ -191,7 +275,34 @@ private Call callTopRatedMoviesApi() { @Override public void retryPageLoad() { - loadNextPage(); + loadApi("more"); + } + + @Override + public void onItemsClickListener(Result result, int position) { + Toast.makeText(this, "click position " + position, Toast.LENGTH_SHORT).show(); + Result result1 = new Result(); + result1.setId(19); + result1.setAdult(true); + result1.setOriginalLanguage("MM"); + result1.setOriginalTitle(""); + result1.setReleaseDate("1993"); + result1.setBackdropPath("https://pbs.twimg.com/profile_images/737889751034920960/ATs6TR-T_400x400.jpg"); + result1.setPosterPath("https://pbs.twimg.com/profile_images/737889751034920960/ATs6TR-T_400x400.jpg"); + result1.setTitle("Hein Htet"); + result1.setGenreIds(new ArrayList()); + adapter.updateItemsAtPosition(position, result1); + } + + @Override + public void emptyLayout() { + + } + + private void hideSwipeRefresh() { + if (swipeRefreshLayout.isRefreshing()) { + swipeRefreshLayout.setRefreshing(false); + } } @@ -202,9 +313,9 @@ public void retryPageLoad() { private void showErrorView(Throwable throwable) { if (errorLayout.getVisibility() == View.GONE) { + hideSwipeRefresh(); errorLayout.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); - txtError.setText(fetchErrorMessage(throwable)); } } @@ -244,4 +355,40 @@ private boolean isNetworkConnected() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); return cm.getActiveNetworkInfo() != null; } + + @Override + public void onRefresh() { + isLoading = false; + isLastPage = false; + currentPage = PAGE_START; + adapter.clear(); + loadApi("first"); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.add, menu); + return true; + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.add_row: { + Result r = new Result(); + r.setPosterPath("https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/24862150_10154896375856035_2285275458086808430_n.jpg?oh=b8a8d3c097ad859ee24a2c1818003688&oe=5A8BE160"); + r.setBackdropPath("https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/24862150_10154896375856035_2285275458086808430_n.jpg?oh=b8a8d3c097ad859ee24a2c1818003688&oe=5A8BE160"); + r.setTitle("add row"); + r.setOriginalLanguage("En"); + r.setReleaseDate("13131"); + r.setId(242); + r.setGenreIds(new ArrayList()); + adapter.addRow(r); + } + } + return super.onOptionsItemSelected(item); + } + } diff --git a/app/src/main/java/com/suleiman/pagination/PaginationAdapter.java b/app/src/main/java/com/suleiman/pagination/PaginationAdapter.java index 09aad46..48f3cdf 100644 --- a/app/src/main/java/com/suleiman/pagination/PaginationAdapter.java +++ b/app/src/main/java/com/suleiman/pagination/PaginationAdapter.java @@ -25,16 +25,13 @@ import java.util.ArrayList; import java.util.List; -/** - * Created by Suleiman on 19/10/16. - */ public class PaginationAdapter extends RecyclerView.Adapter { // View Types - private static final int ITEM = 0; - private static final int LOADING = 1; - private static final int HERO = 2; + public static final int ITEM = 0; + public static final int LOADING = 1; + public static final int HERO = 2; private static final String BASE_URL_IMG = "https://image.tmdb.org/t/p/w150"; @@ -90,22 +87,26 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (getItemViewType(position)) { + case HERO: final HeroVH heroVh = (HeroVH) holder; - heroVh.mMovieTitle.setText(result.getTitle()); - heroVh.mYear.setText(formatYearLabel(result)); + if (result.getTitle() != null) { + heroVh.mYear.setText(formatYearLabel(result)); + } else { + heroVh.mYear.setText("null"); + } heroVh.mMovieDesc.setText(result.getOverview()); loadImage(result.getBackdropPath()) .into(heroVh.mPosterImg); break; - case ITEM: final MovieVH movieVH = (MovieVH) holder; - movieVH.mMovieTitle.setText(result.getTitle()); - movieVH.mYear.setText(formatYearLabel(result)); + if (result.getTitle() != null) { + movieVH.mYear.setText(formatYearLabel(result)); + } movieVH.mMovieDesc.setText(result.getOverview()); // load movie thumbnail @@ -130,7 +131,6 @@ public boolean onResourceReady(GlideDrawable resource, String model, Target + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_hero.xml b/app/src/main/res/layout/item_hero.xml index 4c65bba..7329358 100644 --- a/app/src/main/res/layout/item_hero.xml +++ b/app/src/main/res/layout/item_hero.xml @@ -1,6 +1,5 @@ - @@ -18,13 +18,15 @@ android:id="@+id/movie_poster" android:layout_width="match_parent" android:layout_height="match_parent" - tools:background="@color/colorAccent"/> + android:adjustViewBounds="true" + android:foreground="?attr/selectableItemBackground" + tools:background="@color/colorAccent" /> + android:background="@drawable/scrim" /> + tools:text="Doctor Who : The Day of the Doctor" /> + tools:text="2009 | EN" /> @@ -72,7 +74,7 @@ android:ellipsize="end" android:maxLines="2" android:textColor="@android:color/white" - tools:text="Nowadays, user engagement is considered one of the most important metrics for the success of your app"/> + tools:text="Nowadays, user engagement is considered one of the most important metrics for the success of your app" /> diff --git a/app/src/main/res/layout/item_list.xml b/app/src/main/res/layout/item_list.xml index 7f1a783..c99c19c 100644 --- a/app/src/main/res/layout/item_list.xml +++ b/app/src/main/res/layout/item_list.xml @@ -1,10 +1,10 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:theme="@style/CircularProgress" /> + android:layout_height="@dimen/poster_thumb_height" /> @@ -54,7 +54,7 @@ android:gravity="end" android:maxLines="1" android:textStyle="bold" - tools:text="2009 | EN"/> + tools:text="2009 | EN" /> + tools:text="Movie Title" /> + tools:text="Nowadays, user engagement is considered one of the most important metrics for the success of your app" /> diff --git a/app/src/main/res/menu/add.xml b/app/src/main/res/menu/add.xml new file mode 100644 index 0000000..9c75ef7 --- /dev/null +++ b/app/src/main/res/menu/add.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 46cf8ff..a3997c4 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,6 @@ - #5F4BB6 + #030303 #433582 #26F0F1 #EFEFEF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5b87003..b970216 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Pagination + Recycler View Paginate and More ec01f8c2eb6ac402f2ca026dc2d9b8fd Sorry! Couldn\'t fetch movies. Retry diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 694e6b7..a8c06d7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,4 +1,4 @@ - +