Skip to main content

[Make-A] Movies app using TMDb API Part 6 – Sort Movies by Popular, Top Rated and Upcoming

 

Currently, we show the movies sorted by popularity. But what if your users want to know the latest/upcoming movies this year or top rated movies? We should provide flexibility for our users to give them a much better experience using our app.

TMDb API offers five sorting options for our movies:

  • Popular
  • Top Rated
  • Upcoming
  • Latest
  • Now Playing

For this tutorial we will only use three – Popular, Top Rated and Upcoming. The remaining two will be left as an exercise.

Our sorting options will be presented using a PopupMenu. Here’s how we will implement our sorting feature:

Table of Contents

  1. Part 1 – Specs and Introduction
  2. Part 2 – Requesting an API Key
  3. Part 3 – Movie List Item Layout Using ConstraintLayout
  4. Part 4 – Networking using Retrofit Library and Image Loading using Glide
  5. Part 5 – Pagination
  6. Part 6 – Sort Movies by Popular, Top Rated and Upcoming
  7. Part 7 – Using CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout for Movie Details Screen
  8. Part 8 – Colors and a Free Launcher Icon Generator
  9. 5 Exercises and Thanks!

1. Building our Menu

1. Open your strings.xml and add 3 strings

<resources>
    <string name="app_name">ImapMovies</string>
    <string name="popular">Popular</string>
    <string name="top_rated">Top Rated</string>
    <string name="upcoming">Upcoming</string>
</resources>

2. Download our sort icon PNGs here – https://material.io/icons/#ic_sort

3. Extract the .zip folder and copy paste it under your res/ directory.

This is similar to how we added an icon for our FAB in our chat application.

4. Right click res/ directory. New -> Android resource directory

5. Set directory name to menu and resource type to menu

6. After you’ve created your menu resource directory, right click -> New -> Menu resource file

7. Set file name to menu_movies. Add a menu item

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/sort"
        android:enabled="true"
        android:icon="@drawable/ic_sort_white_24dp"
        android:title="Sort"
        app:showAsAction="always" />
</menu>

8. Open your MainActivity.class. Override onCreateOptionsMenu(…) and inflate our menu

public class MainActivity extends AppCompatActivity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_movies, menu);
        return super.onCreateOptionsMenu(menu);
    }

    ...
}

9. Run the app and you should see your sort icon! 🎉

 

2. Show sorting options using a PopupMenu

1. Create a new menu resource file called menu_movies_sort. Right click menu folder under your res folder. New -> menu resource file

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/popular"
        android:enabled="true"
        android:title="Popular"
        app:showAsAction="never" />
    <item
        android:id="@+id/top_rated"
        android:enabled="true"
        android:title="Top Rated"
        app:showAsAction="never" />
    <item
        android:id="@+id/upcoming"
        android:enabled="true"
        android:title="Upcoming"
        app:showAsAction="never" />
</menu>

2. Open your MainActivity.class. Override onOptionsItemSelected(…)

public class MainActivity extends AppCompatActivity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    ...

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.sort:
                showSortMenu();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void showSortMenu() {
        PopupMenu sortMenu = new PopupMenu(this, findViewById(R.id.sort));
        sortMenu.inflate(R.menu.menu_movies_sort);
        sortMenu.show();
    }

    ...
}

3. Run the app and try clicking your sort icon. You should see your sorting options! 🎉

 

3.  Fetching popular, upcoming and top rated movies using TMDb API

1. Open your TMDbApi interface. Add two new methods

public interface TMDbApi {

    @GET("movie/popular")
    Call<MoviesResponse> getPopularMovies(
            @Query("api_key") String apiKey,
            @Query("language") String language,
            @Query("page") int page
    );

    @GET("movie/top_rated")
    Call<MoviesResponse> getTopRatedMovies(
            @Query("api_key") String apiKey,
            @Query("language") String language,
            @Query("page") int page
    );

    @GET("movie/upcoming")
    Call<MoviesResponse> getUpcomingMovies(
            @Query("api_key") String apiKey,
            @Query("language") String language,
            @Query("page") int page
    );

    @GET("genre/movie/list")
    Call<GenresResponse> getGenres(
            @Query("api_key") String apiKey,
            @Query("language") String language
    );
}

2. Open MoviesRepository.class and add three new static fields

public static final String POPULAR = "popular";
public static final String TOP_RATED = "top_rated";
public static final String UPCOMING = "upcoming";

3. Let’s refactor our MoviesRepository.getMovies(…) method to support sorting options

public class MoviesRepository {

    ...

    public void getMovies(int page, String sortBy, final OnGetMoviesCallback callback) {
        Callback<MoviesResponse> call = new Callback<MoviesResponse>() {
            @Override
            public void onResponse(Call<MoviesResponse> call, Response<MoviesResponse> response) {
                if (response.isSuccessful()) {
                    MoviesResponse moviesResponse = response.body();
                    if (moviesResponse != null && moviesResponse.getMovies() != null) {
                        callback.onSuccess(moviesResponse.getPage(), moviesResponse.getMovies());
                    } else {
                        callback.onError();
                    }
                } else {
                    callback.onError();
                }
            }

            @Override
            public void onFailure(Call<MoviesResponse> call, Throwable t) {
                callback.onError();
            }
        };

        switch (sortBy) {
            case TOP_RATED:
                api.getTopRatedMovies(BuildConfig.TMDB_API_KEY, LANGUAGE, page)
                        .enqueue(call);
                break;
            case UPCOMING:
                api.getUpcomingMovies(BuildConfig.TMDB_API_KEY, LANGUAGE, page)
                        .enqueue(call);
                break;
            case POPULAR:
            default:
                api.getPopularMovies(BuildConfig.TMDB_API_KEY, LANGUAGE, page)
                        .enqueue(call);
                break;
        }
    }

    ...
}

4. Open your MainActivity.class and add a new field call sortBy

private String sortBy = MoviesRepository.POPULAR;

5. In MainActivity.showSortMenu() method, add a PopupMenu.OnMenuItemClickListener() to our PopupMenu

private void showSortMenu() {
    PopupMenu sortMenu = new PopupMenu(this, findViewById(R.id.sort));
    sortMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            /*
             * Every time we sort, we need to go back to page 1
             */
            currentPage = 1;

            switch (item.getItemId()) {
                case R.id.popular:
                    sortBy = MoviesRepository.POPULAR;
                    getMovies(currentPage);
                    return true;
                case R.id.top_rated:
                    sortBy = MoviesRepository.TOP_RATED;
                    getMovies(currentPage);
                    return true;
                case R.id.upcoming:
                    sortBy = MoviesRepository.UPCOMING;
                    getMovies(currentPage);
                    return true;
                default:
                    return false;
            }
        }
    });
    sortMenu.inflate(R.menu.menu_movies_sort);
    sortMenu.show();
}

6. Open your MoviesAdapter.class and add a new method

public void clearMovies() {
    movies.clear();
    notifyDataSetChanged();
}

7. In your MainActivity.getMovies(…), pass the sortBy field to clear the error and let’s clear the list if we are back to page 1 which tells us that we changed our sorting option.

private void getMovies(int page) {
    isFetchingMovies = true;
    moviesRepository.getMovies(page, sortBy, new OnGetMoviesCallback() {
        @Override
        public void onSuccess(int page, List<Movie> movies) {
            if (adapter == null) {
                adapter = new MoviesAdapter(movies, movieGenres);
                moviesList.setAdapter(adapter);
            } else {
                if (page == 1) {
                    adapter.clearMovies();
                }
                adapter.appendMovies(movies);
            }
            currentPage = page;
            isFetchingMovies = false;
        }

        @Override
        public void onError() {
            showError();
        }
    });
}

 

3. Let’s test it out

Run the app. As you can see, you can now change the sorting options 🎉🎉🎉

 

Part 7 coming soon.