├── app ├── .gitignore ├── src │ └── main │ │ ├── web_hi_res_512.png │ │ ├── res │ │ ├── drawable │ │ │ ├── baps.webp │ │ │ ├── github.webp │ │ │ ├── play_store.webp │ │ │ ├── splash_screen_logo.webp │ │ │ ├── tmdb_placeholder.webp │ │ │ ├── tmdb_placeholder_land.webp │ │ │ ├── cursor_search.xml │ │ │ ├── shadow.xml │ │ │ ├── ic_expand_less_white_24dp.xml │ │ │ ├── ic_expand_more_white_24dp.xml │ │ │ ├── ic_arrow_back_white_24dp.xml │ │ │ ├── ic_play_circle_filled_black_24dp.xml │ │ │ ├── ic_favorite.xml │ │ │ ├── ic_info_outline_white_24dp.xml │ │ │ ├── ic_cancel_white_24dp.xml │ │ │ ├── ic_favorite_black_24dp.xml │ │ │ ├── ic_hourglass_empty_black_24dp.xml │ │ │ ├── ic_stars_black_24dp.xml │ │ │ ├── ic_keyboard_voice_white_24dp.xml │ │ │ ├── ic_search_white_24dp.xml │ │ │ ├── ic_favorite_border.xml │ │ │ ├── ic_timeline_black_24dp.xml │ │ │ └── ic_language_white_36dp.xml │ │ ├── font │ │ │ └── hammersmith_one.ttf │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── imdb_icon.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── imdb_icon.webp │ │ ├── mipmap-xhdpi │ │ │ ├── imdb_icon.webp │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── imdb_icon.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── imdb_icon.webp │ │ │ └── ic_launcher.png │ │ ├── drawable-hdpi │ │ │ ├── ic_stat_notification.webp │ │ │ ├── no_internet_placeholder.webp │ │ │ └── no_internet_placeholder_landscape.webp │ │ ├── drawable-mdpi │ │ │ ├── ic_stat_notification.png │ │ │ ├── no_internet_placeholder.webp │ │ │ └── no_internet_placeholder_landscape.webp │ │ ├── drawable-xhdpi │ │ │ ├── ic_stat_notification.webp │ │ │ ├── no_internet_placeholder.webp │ │ │ └── no_internet_placeholder_landscape.webp │ │ ├── drawable-xxhdpi │ │ │ ├── ic_stat_notification.webp │ │ │ ├── no_internet_placeholder.webp │ │ │ └── no_internet_placeholder_landscape.webp │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_stat_notification.webp │ │ │ ├── no_internet_placeholder.webp │ │ │ └── no_internet_placeholder_landscape.webp │ │ ├── color │ │ │ └── selector.xml │ │ ├── anim │ │ │ └── bounce.xml │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── menu │ │ │ ├── menu_main.xml │ │ │ └── menu_bottom_nav.xml │ │ └── layout │ │ │ ├── rv_genre_item.xml │ │ │ ├── rv_movie_item.xml │ │ │ ├── rv_cast.xml │ │ │ ├── activity_splash_screen.xml │ │ │ ├── rv_trailer_item.xml │ │ │ ├── rv_review_item.xml │ │ │ ├── activity_about_me.xml │ │ │ ├── activity_main.xml │ │ │ └── activity_details.xml │ │ ├── java │ │ └── bapspatil │ │ │ └── silverscreener │ │ │ ├── utils │ │ │ ├── SilverScreenerGlideModule.java │ │ │ ├── NetworkUtils.java │ │ │ └── EndlessScrollListener.java │ │ │ ├── SilverScreenerApp.java │ │ │ ├── data │ │ │ └── RealmDataSource.java │ │ │ ├── ui │ │ │ ├── SplashScreenActivity.java │ │ │ ├── AboutMeActivity.java │ │ │ ├── MainActivity.java │ │ │ └── DetailsActivity.java │ │ │ ├── model │ │ │ ├── TMDBReviewResponse.java │ │ │ ├── TMDBTrailerResponse.java │ │ │ ├── Crew.java │ │ │ ├── Cast.java │ │ │ ├── Review.java │ │ │ ├── GenresItem.java │ │ │ ├── Trailer.java │ │ │ ├── TMDBCreditsResponse.java │ │ │ ├── MovieRecyclerView.java │ │ │ ├── TMDBResponse.java │ │ │ ├── Movie.java │ │ │ └── TMDBDetailsResponse.java │ │ │ ├── adapters │ │ │ ├── GenresRecyclerViewAdapter.java │ │ │ ├── CastRecyclerViewAdapter.java │ │ │ ├── MovieRecyclerViewAdapter.java │ │ │ ├── ReviewRecyclerViewAdapter.java │ │ │ └── TrailerRecyclerViewAdapter.java │ │ │ └── network │ │ │ └── RetrofitAPI.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── design ├── screen0.png ├── screen1.png ├── screen2.png ├── screen3.png ├── screen4.png └── screen5.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /design/screen0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen0.png -------------------------------------------------------------------------------- /design/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen1.png -------------------------------------------------------------------------------- /design/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen2.png -------------------------------------------------------------------------------- /design/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen3.png -------------------------------------------------------------------------------- /design/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen4.png -------------------------------------------------------------------------------- /design/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/design/screen5.png -------------------------------------------------------------------------------- /app/src/main/web_hi_res_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/web_hi_res_512.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/baps.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/baps.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/github.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/github.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/play_store.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/play_store.webp -------------------------------------------------------------------------------- /app/src/main/res/font/hammersmith_one.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/font/hammersmith_one.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/imdb_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-hdpi/imdb_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/imdb_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-mdpi/imdb_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/imdb_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xhdpi/imdb_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/imdb_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xxhdpi/imdb_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/imdb_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xxxhdpi/imdb_icon.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_screen_logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/splash_screen_logo.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/tmdb_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/tmdb_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/tmdb_placeholder_land.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable/tmdb_placeholder_land.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_notification.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-hdpi/ic_stat_notification.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-mdpi/ic_stat_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/no_internet_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-hdpi/no_internet_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/no_internet_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-mdpi/no_internet_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_notification.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xhdpi/ic_stat_notification.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_notification.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxhdpi/ic_stat_notification.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notification.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notification.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/no_internet_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xhdpi/no_internet_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/no_internet_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxhdpi/no_internet_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/no_internet_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxxhdpi/no_internet_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/no_internet_placeholder_landscape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-hdpi/no_internet_placeholder_landscape.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/no_internet_placeholder_landscape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-mdpi/no_internet_placeholder_landscape.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/no_internet_placeholder_landscape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xhdpi/no_internet_placeholder_landscape.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/no_internet_placeholder_landscape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxhdpi/no_internet_placeholder_landscape.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/no_internet_placeholder_landscape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bapspatil/SilverScreener/HEAD/app/src/main/res/drawable-xxxhdpi/no_internet_placeholder_landscape.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | /gradle.properties 10 | app/release/ 11 | app/src/main/res/layout-land/ 12 | app/google-services.json 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cursor_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Dec 26 12:28:34 IST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/color/selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/utils/SilverScreenerGlideModule.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.utils; 2 | 3 | import com.bumptech.glide.annotation.GlideModule; 4 | import com.bumptech.glide.module.AppGlideModule; 5 | 6 | /** 7 | * Created by bapspatil 8 | */ 9 | 10 | @GlideModule 11 | public class SilverScreenerGlideModule extends AppGlideModule { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_expand_less_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_expand_more_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/anim/bounce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_circle_filled_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #323846 4 | #0c121f 5 | #fbf9f3 6 | #818181 7 | #929292 8 | #FFFFFF 9 | #000000 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_outline_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cancel_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hourglass_empty_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stars_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_keyboard_voice_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_favorite_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/SilverScreenerApp.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener; 2 | 3 | import android.app.Application; 4 | 5 | import io.realm.Realm; 6 | import io.realm.RealmConfiguration; 7 | 8 | /** 9 | * Created by bapspatil 10 | */ 11 | 12 | public class SilverScreenerApp extends Application { 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | Realm.init(this); 18 | RealmConfiguration realmConfig = new RealmConfiguration.Builder() 19 | .name("silverscreener.realm") 20 | .deleteRealmIfMigrationNeeded() 21 | .build(); 22 | Realm.setDefaultConfiguration(realmConfig); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_timeline_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_genre_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_movie_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_language_white_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SilverScreener 3 | Poster Of The Movie 4 | Popular 5 | Top Rated 6 | Sort By 7 | Backdrop of the movie 8 | Looks like no one bothered to write reviews for this movie! :-P 9 | Search 10 | REVIEWS 11 | CAST 12 | Cast Images 13 | About The Developer 14 | This is Bapusaheb Patil 15 | Bapusaheb Patil 16 | Hi! I am a Google-certified Android Developer and an IDF-certified UX Designer.\n\nI love making apps, watchfaces and memes.\n\nNot specifically in that order.\n\nSee more of my apps and my work here: 17 | Voice Search 18 | 19 | 20 | Most Popular 21 | Highest Rated 22 | Favorites 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_bottom_nav.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 17 | 23 | 29 | 30 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/data/RealmDataSource.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.data; 2 | 3 | import java.util.ArrayList; 4 | 5 | import bapspatil.silverscreener.model.Movie; 6 | import io.realm.Realm; 7 | import io.realm.RealmResults; 8 | 9 | /** 10 | * Created by bapspatil 11 | */ 12 | 13 | public class RealmDataSource { 14 | private Realm realm; 15 | 16 | public RealmDataSource() { 17 | // Empty constructor 18 | } 19 | 20 | public void open() { 21 | realm = Realm.getDefaultInstance(); 22 | } 23 | 24 | public void close() { 25 | realm.close(); 26 | } 27 | 28 | public ArrayList getAllFavMovies() { 29 | RealmResults movies = realm.where(Movie.class).findAll(); 30 | ArrayList movieArrayList = new ArrayList<>(); 31 | movieArrayList.addAll(realm.copyFromRealm(movies)); 32 | return movieArrayList; 33 | } 34 | 35 | public Movie findMovieWithId(int id) { 36 | return realm.where(Movie.class) 37 | .equalTo("id", id) 38 | .findFirst(); 39 | } 40 | 41 | public void addMovieToFavs(final Movie movie) { 42 | realm.executeTransaction(realm -> realm.insertOrUpdate(movie)); 43 | } 44 | 45 | public void deleteMovieFromFavs(final Movie movie) { 46 | realm.executeTransaction(realm -> movie.deleteFromRealm()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_cast.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | 24 | 25 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/ui/SplashScreenActivity.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.view.View; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import bapspatil.silverscreener.R; 11 | 12 | public class SplashScreenActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_splash_screen); 18 | hideSystemUI(); 19 | int SPLASH_TIME_OUT = 1000; 20 | new Handler().postDelayed(() -> { 21 | Intent i = new Intent(SplashScreenActivity.this, MainActivity.class); 22 | startActivity(i); 23 | finish(); 24 | }, SPLASH_TIME_OUT); 25 | } 26 | 27 | private void hideSystemUI() { 28 | getWindow().getDecorView().setSystemUiVisibility( 29 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 30 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 31 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 32 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar 33 | | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar 34 | | View.SYSTEM_UI_FLAG_IMMERSIVE); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/TMDBReviewResponse.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by bapspatil 12 | */ 13 | 14 | public class TMDBReviewResponse implements Parcelable { 15 | public static final Creator CREATOR = new Creator() { 16 | @Override 17 | public TMDBReviewResponse createFromParcel(Parcel source) { 18 | return new TMDBReviewResponse(source); 19 | } 20 | 21 | @Override 22 | public TMDBReviewResponse[] newArray(int size) { 23 | return new TMDBReviewResponse[size]; 24 | } 25 | }; 26 | @SerializedName("results") 27 | private ArrayList results; 28 | 29 | public TMDBReviewResponse() { 30 | } 31 | 32 | protected TMDBReviewResponse(Parcel in) { 33 | this.results = in.createTypedArrayList(Review.CREATOR); 34 | } 35 | 36 | public ArrayList getResults() { 37 | return results; 38 | } 39 | 40 | public void setResults(ArrayList results) { 41 | this.results = results; 42 | } 43 | 44 | @Override 45 | public int describeContents() { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public void writeToParcel(Parcel dest, int flags) { 51 | dest.writeTypedList(this.results); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/TMDBTrailerResponse.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by bapspatil 12 | */ 13 | 14 | public class TMDBTrailerResponse implements Parcelable { 15 | public static final Creator CREATOR = new Creator() { 16 | @Override 17 | public TMDBTrailerResponse createFromParcel(Parcel source) { 18 | return new TMDBTrailerResponse(source); 19 | } 20 | 21 | @Override 22 | public TMDBTrailerResponse[] newArray(int size) { 23 | return new TMDBTrailerResponse[size]; 24 | } 25 | }; 26 | @SerializedName("results") 27 | private ArrayList results; 28 | 29 | public TMDBTrailerResponse() { 30 | } 31 | 32 | protected TMDBTrailerResponse(Parcel in) { 33 | this.results = in.createTypedArrayList(Trailer.CREATOR); 34 | } 35 | 36 | public ArrayList getResults() { 37 | return results; 38 | } 39 | 40 | public void setResults(ArrayList results) { 41 | this.results = results; 42 | } 43 | 44 | @Override 45 | public int describeContents() { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public void writeToParcel(Parcel dest, int flags) { 51 | dest.writeTypedList(this.results); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/Crew.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * Created by bapspatil on 11/17/17. 10 | */ 11 | 12 | public class Crew implements Parcelable { 13 | public static final Creator CREATOR = new Creator() { 14 | @Override 15 | public Crew createFromParcel(Parcel source) { 16 | return new Crew(source); 17 | } 18 | 19 | @Override 20 | public Crew[] newArray(int size) { 21 | return new Crew[size]; 22 | } 23 | }; 24 | @SerializedName("job") 25 | private String job; 26 | @SerializedName("name") 27 | private String name; 28 | 29 | public Crew() { 30 | } 31 | 32 | protected Crew(Parcel in) { 33 | this.job = in.readString(); 34 | this.name = in.readString(); 35 | } 36 | 37 | public String getJob() { 38 | return job; 39 | } 40 | 41 | public void setJob(String job) { 42 | this.job = job; 43 | } 44 | 45 | public String getName() { 46 | return name; 47 | } 48 | 49 | public void setName(String name) { 50 | this.name = name; 51 | } 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | @Override 59 | public void writeToParcel(Parcel dest, int flags) { 60 | dest.writeString(this.job); 61 | dest.writeString(this.name); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/Cast.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * Created by bapspatil 10 | */ 11 | 12 | public class Cast implements Parcelable { 13 | public static final Creator CREATOR = new Creator() { 14 | @Override 15 | public Cast createFromParcel(Parcel source) { 16 | return new Cast(source); 17 | } 18 | 19 | @Override 20 | public Cast[] newArray(int size) { 21 | return new Cast[size]; 22 | } 23 | }; 24 | @SerializedName("name") 25 | private String name; 26 | @SerializedName("profile_path") 27 | private String profilePath; 28 | 29 | public Cast() { 30 | } 31 | 32 | protected Cast(Parcel in) { 33 | this.name = in.readString(); 34 | this.profilePath = in.readString(); 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public String getProfilePath() { 46 | return profilePath; 47 | } 48 | 49 | public void setProfilePath(String profilePath) { 50 | this.profilePath = profilePath; 51 | } 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | @Override 59 | public void writeToParcel(Parcel dest, int flags) { 60 | dest.writeString(this.name); 61 | dest.writeString(this.profilePath); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/Review.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * Created by bapspatil 10 | */ 11 | 12 | public class Review implements Parcelable { 13 | public static final Creator CREATOR = new Creator() { 14 | @Override 15 | public Review createFromParcel(Parcel source) { 16 | return new Review(source); 17 | } 18 | 19 | @Override 20 | public Review[] newArray(int size) { 21 | return new Review[size]; 22 | } 23 | }; 24 | @SerializedName("author") 25 | private String author; 26 | @SerializedName("content") 27 | private String content; 28 | 29 | public Review() { 30 | } 31 | 32 | protected Review(Parcel in) { 33 | this.author = in.readString(); 34 | this.content = in.readString(); 35 | } 36 | 37 | public String getAuthor() { 38 | return author; 39 | } 40 | 41 | public void setAuthor(String author) { 42 | this.author = author; 43 | } 44 | 45 | public String getContent() { 46 | return content; 47 | } 48 | 49 | public void setContent(String content) { 50 | this.content = content; 51 | } 52 | 53 | @Override 54 | public int describeContents() { 55 | return 0; 56 | } 57 | 58 | @Override 59 | public void writeToParcel(Parcel dest, int flags) { 60 | dest.writeString(this.author); 61 | dest.writeString(this.content); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/GenresItem.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | public class GenresItem implements Parcelable { 9 | 10 | public static final Creator CREATOR = new Creator() { 11 | @Override 12 | public GenresItem createFromParcel(Parcel source) { 13 | return new GenresItem(source); 14 | } 15 | 16 | @Override 17 | public GenresItem[] newArray(int size) { 18 | return new GenresItem[size]; 19 | } 20 | }; 21 | @SerializedName("name") 22 | private String name; 23 | @SerializedName("id") 24 | private int id; 25 | 26 | public GenresItem() { 27 | } 28 | 29 | public GenresItem(String name, int id) { 30 | 31 | this.name = name; 32 | this.id = id; 33 | } 34 | 35 | protected GenresItem(Parcel in) { 36 | this.name = in.readString(); 37 | this.id = in.readInt(); 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | public int getId() { 49 | return id; 50 | } 51 | 52 | public void setId(int id) { 53 | this.id = id; 54 | } 55 | 56 | @Override 57 | public int describeContents() { 58 | return 0; 59 | } 60 | 61 | @Override 62 | public void writeToParcel(Parcel dest, int flags) { 63 | dest.writeString(this.name); 64 | dest.writeInt(this.id); 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/Trailer.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * Created by bapspatil 10 | */ 11 | 12 | public class Trailer implements Parcelable { 13 | public static final Creator CREATOR = new Creator() { 14 | @Override 15 | public Trailer createFromParcel(Parcel source) { 16 | return new Trailer(source); 17 | } 18 | 19 | @Override 20 | public Trailer[] newArray(int size) { 21 | return new Trailer[size]; 22 | } 23 | }; 24 | @SerializedName("name") 25 | private String name; 26 | @SerializedName("key") 27 | private String key; 28 | 29 | public Trailer() { 30 | } 31 | 32 | protected Trailer(Parcel in) { 33 | this.name = in.readString(); 34 | this.key = in.readString(); 35 | } 36 | 37 | public static Creator getCREATOR() { 38 | return CREATOR; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public void setName(String name) { 46 | this.name = name; 47 | } 48 | 49 | public String getKey() { 50 | return key; 51 | } 52 | 53 | public void setKey(String key) { 54 | this.key = key; 55 | } 56 | 57 | @Override 58 | public int describeContents() { 59 | return 0; 60 | } 61 | 62 | @Override 63 | public void writeToParcel(Parcel dest, int flags) { 64 | dest.writeString(this.name); 65 | dest.writeString(this.key); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_trailer_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 25 | 26 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/TMDBCreditsResponse.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by bapspatil 12 | */ 13 | 14 | public class TMDBCreditsResponse implements Parcelable { 15 | public static final Creator CREATOR = new Creator() { 16 | @Override 17 | public TMDBCreditsResponse createFromParcel(Parcel source) { 18 | return new TMDBCreditsResponse(source); 19 | } 20 | 21 | @Override 22 | public TMDBCreditsResponse[] newArray(int size) { 23 | return new TMDBCreditsResponse[size]; 24 | } 25 | }; 26 | @SerializedName("cast") 27 | private ArrayList cast; 28 | @SerializedName("crew") 29 | private ArrayList crew; 30 | 31 | public TMDBCreditsResponse() { 32 | } 33 | 34 | protected TMDBCreditsResponse(Parcel in) { 35 | this.cast = in.createTypedArrayList(Cast.CREATOR); 36 | this.crew = in.createTypedArrayList(Crew.CREATOR); 37 | } 38 | 39 | public ArrayList getCast() { 40 | return cast; 41 | } 42 | 43 | public void setCast(ArrayList cast) { 44 | this.cast = cast; 45 | } 46 | 47 | public ArrayList getCrew() { 48 | return crew; 49 | } 50 | 51 | public void setCrew(ArrayList crew) { 52 | this.crew = crew; 53 | } 54 | 55 | @Override 56 | public int describeContents() { 57 | return 0; 58 | } 59 | 60 | @Override 61 | public void writeToParcel(Parcel dest, int flags) { 62 | dest.writeTypedList(this.cast); 63 | dest.writeTypedList(this.crew); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/utils/NetworkUtils.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.utils; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | import bapspatil.silverscreener.network.RetrofitAPI; 8 | import okhttp3.Cache; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.Request; 11 | import retrofit2.Retrofit; 12 | import retrofit2.converter.gson.GsonConverterFactory; 13 | 14 | public class NetworkUtils { 15 | 16 | public static Boolean hasNetwork(Context context) { 17 | Boolean isConnected = false; // Initial Value 18 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 19 | NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); 20 | if (activeNetwork != null && activeNetwork.isConnectedOrConnecting()) 21 | isConnected = true; 22 | return isConnected; 23 | } 24 | 25 | public static Retrofit getCacheEnabledRetrofit(final Context context) { 26 | OkHttpClient okHttpClient = new OkHttpClient.Builder() 27 | .cache(new Cache(context.getCacheDir(), 5 * 1024 * 1024)) 28 | .addInterceptor(chain -> { 29 | Request request = chain.request(); 30 | if (hasNetwork(context)) 31 | request = request.newBuilder().header("Cache-Control", "public, max-age=" + 1).build(); 32 | else 33 | request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build(); 34 | return chain.proceed(request); 35 | }) 36 | .build(); 37 | 38 | return new Retrofit.Builder() 39 | .addConverterFactory(GsonConverterFactory.create()) 40 | .client(okHttpClient) 41 | .baseUrl(RetrofitAPI.BASE_URL) 42 | .build(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/adapters/GenresRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import androidx.core.content.res.ResourcesCompat; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import java.util.ArrayList; 14 | 15 | import bapspatil.silverscreener.R; 16 | import bapspatil.silverscreener.model.GenresItem; 17 | import butterknife.BindView; 18 | import butterknife.ButterKnife; 19 | 20 | /** 21 | * Created by bapspatil 22 | */ 23 | 24 | public class GenresRecyclerViewAdapter extends RecyclerView.Adapter { 25 | 26 | private Context mContext; 27 | private ArrayList mGenres; 28 | 29 | public GenresRecyclerViewAdapter(Context context, ArrayList genres) { 30 | this.mContext = context; 31 | this.mGenres = genres; 32 | } 33 | 34 | @Override 35 | public GenreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 36 | View view = LayoutInflater.from(mContext).inflate(R.layout.rv_genre_item, parent, false); 37 | return new GenreViewHolder(view); 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(GenreViewHolder holder, int position) { 42 | Typeface hammersmithOne = ResourcesCompat.getFont(mContext, R.font.hammersmith_one); 43 | holder.mGenreTextView.setTypeface(hammersmithOne); 44 | holder.mGenreTextView.setText(mGenres.get(position).getName()); 45 | } 46 | 47 | @Override 48 | public int getItemCount() { 49 | if (mGenres != null) 50 | return mGenres.size(); 51 | else 52 | return 0; 53 | } 54 | 55 | public class GenreViewHolder extends RecyclerView.ViewHolder { 56 | @BindView(R.id.genre_tv) 57 | TextView mGenreTextView; 58 | 59 | public GenreViewHolder(View itemView) { 60 | super(itemView); 61 | ButterKnife.bind(this, itemView); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # SilverScreener 3 | 4 | A feature-rich movie guide app, that lets you discover movies from TMDb. 5 | 6 | Get it on Google Play 7 | 8 | ## Screenshots 9 | 10 | 11 | 12 | 13 | 14 | ## Libraries Used 15 | 16 | * [Android Support Library](https://developer.android.com/topic/libraries/support-library/) 17 | * [Firebase](https://firebase.google.com/) 18 | * [Retrofit](https://github.com/square/retrofit/) 19 | * [Glide](https://github.com/bumptech/glide/) 20 | * [CookieBar2](https://github.com/AviranAbady/CookieBar2) 21 | * [Gson](https://github.com/google/gson/) 22 | * [ButterKnife](https://github.com/JakeWharton/butterknife) 23 | * [recyclerview-animators](https://github.com/wasabeef/recyclerview-animators/) 24 | * [Android-ExpandableTextView](https://github.com/Blogcat/Android-ExpandableTextView) 25 | * [floatingsearchview](https://github.com/arimorty/floatingsearchview) 26 | * [Realm Database](http://realm.io/) 27 | 28 | ## Developed By 29 | 30 | Bapusaheb Patil 31 | 32 | 33 | 34 | https://bapspatil.com 35 | 36 | ## License 37 | 38 | Copyright 2018 Bapusaheb Patil 39 | 40 | Licensed under the Apache License, Version 2.0 (the "License"); 41 | you may not use this file except in compliance with the License. 42 | You may obtain a copy of the License at 43 | 44 | http://www.apache.org/licenses/LICENSE-2.0 45 | 46 | Unless required by applicable law or agreed to in writing, software 47 | distributed under the License is distributed on an "AS IS" BASIS, 48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 49 | See the License for the specific language governing permissions and 50 | limitations under the License. 51 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/ui/AboutMeActivity.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.ui; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.widget.ImageView; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.appcompat.widget.Toolbar; 11 | import androidx.core.content.ContextCompat; 12 | 13 | import com.bumptech.glide.Glide; 14 | 15 | import bapspatil.silverscreener.R; 16 | import butterknife.BindView; 17 | import butterknife.ButterKnife; 18 | 19 | public class AboutMeActivity extends AppCompatActivity { 20 | 21 | @BindView(R.id.toolbar) 22 | Toolbar toolbar; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_about_me); 28 | ButterKnife.bind(this); 29 | getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimary)); 30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 31 | getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.colorPrimary)); 32 | } 33 | toolbar.setTitle(""); 34 | setSupportActionBar(toolbar); 35 | (findViewById(R.id.play_iv)).setOnClickListener(view -> { 36 | Uri uri = Uri.parse("https://play.google.com/store/apps/dev?id=7368032842071222295"); 37 | Intent intentToPlayStore = new Intent(Intent.ACTION_VIEW, uri); 38 | startActivity(intentToPlayStore); 39 | }); 40 | (findViewById(R.id.github_iv)).setOnClickListener(view -> { 41 | Uri uri = Uri.parse("https://github.com/bapspatil"); 42 | Intent intentToGitHub = new Intent(Intent.ACTION_VIEW, uri); 43 | startActivity(intentToGitHub); 44 | }); 45 | Glide.with(this) 46 | .load("https://github.com/bapspatil.png") 47 | .into((ImageView) findViewById(R.id.bapspatil_iv)); 48 | } 49 | 50 | @Override 51 | public void onBackPressed() { 52 | super.onBackPressed(); 53 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\bapoo\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | 27 | # For Glide 28 | -keep public class * implements com.bumptech.glide.module.GlideModule 29 | -keep public class * extends com.bumptech.glide.AppGlideModule 30 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 31 | **[] $VALUES; 32 | public *; 33 | } 34 | 35 | # For Realm 36 | -keep class io.realm.annotations.RealmModule 37 | -keep @io.realm.annotations.RealmModule class * 38 | -keep class io.realm.internal.Keep 39 | -keep @io.realm.internal.Keep class * 40 | -dontwarn javax.** 41 | -dontwarn io.realm.** 42 | 43 | # For Retrofit 44 | -dontwarn okio.** 45 | -dontwarn javax.annotation.** 46 | 47 | -keep class com.google.gson.** { *; } 48 | -keep class com.google.inject.** { *; } 49 | 50 | -keep class org.apache.http.** { *; } 51 | -keep class org.apache.james.mime4j.** { *; } 52 | 53 | -keep class javax.inject.** { *; } 54 | -keep class javax.xml.stream.** { *; } 55 | 56 | -keep class retrofit.** { *; } 57 | 58 | -keep class com.google.appengine.** { *; } 59 | 60 | -keepattributes *Annotation* 61 | -keepattributes Signature 62 | 63 | -dontwarn com.squareup.okhttp.* 64 | -dontwarn rx.** 65 | 66 | -dontwarn javax.xml.stream.** 67 | -dontwarn com.google.appengine.** 68 | -dontwarn java.nio.file.** 69 | -dontwarn org.codehaus.** -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 22 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/MovieRecyclerView.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.os.Parcelable; 6 | import android.util.AttributeSet; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | public final class MovieRecyclerView extends RecyclerView { 12 | 13 | private static final String SAVED_SUPER_STATE = "super-state"; 14 | private static final String SAVED_LAYOUT_MANAGER = "layout-manager-state"; 15 | private Parcelable mLayoutManagerSavedState; 16 | 17 | public MovieRecyclerView(Context context) { 18 | super(context); 19 | } 20 | 21 | public MovieRecyclerView(Context context, @Nullable AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public MovieRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { 26 | super(context, attrs, defStyle); 27 | } 28 | 29 | @Override 30 | protected Parcelable onSaveInstanceState() { 31 | Bundle bundle = new Bundle(); 32 | bundle.putParcelable(SAVED_SUPER_STATE, super.onSaveInstanceState()); 33 | bundle.putParcelable(SAVED_LAYOUT_MANAGER, this.getLayoutManager().onSaveInstanceState()); 34 | return bundle; 35 | } 36 | 37 | @Override 38 | protected void onRestoreInstanceState(Parcelable state) { 39 | if (state instanceof Bundle) { 40 | Bundle bundle = (Bundle) state; 41 | mLayoutManagerSavedState = bundle.getParcelable(SAVED_LAYOUT_MANAGER); 42 | state = bundle.getParcelable(SAVED_SUPER_STATE); 43 | } 44 | super.onRestoreInstanceState(state); 45 | } 46 | 47 | 48 | @Override 49 | public void setAdapter(Adapter adapter) { 50 | super.setAdapter(adapter); 51 | restorePosition(); 52 | } 53 | 54 | /** 55 | * Restores scroll position after configuration change. 56 | * NOTE: Must be called after adapter has been set. 57 | */ 58 | public void restorePosition() { 59 | if (mLayoutManagerSavedState != null) { 60 | this.getLayoutManager().onRestoreInstanceState(mLayoutManagerSavedState); 61 | mLayoutManagerSavedState = null; 62 | } 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/network/RetrofitAPI.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.network; 2 | 3 | import bapspatil.silverscreener.model.TMDBCreditsResponse; 4 | import bapspatil.silverscreener.model.TMDBDetailsResponse; 5 | import bapspatil.silverscreener.model.TMDBResponse; 6 | import bapspatil.silverscreener.model.TMDBReviewResponse; 7 | import bapspatil.silverscreener.model.TMDBTrailerResponse; 8 | import retrofit2.Call; 9 | import retrofit2.Retrofit; 10 | import retrofit2.converter.gson.GsonConverterFactory; 11 | import retrofit2.http.GET; 12 | import retrofit2.http.Path; 13 | import retrofit2.http.Query; 14 | 15 | /** 16 | * Created by bapspatil 17 | */ 18 | 19 | public interface RetrofitAPI { 20 | 21 | String POSTER_BASE_URL = "https://image.tmdb.org/t/p/w342"; 22 | String BACKDROP_BASE_URL = "https://image.tmdb.org/t/p/w500"; 23 | String BASE_URL = "https://api.themoviedb.org/3/"; 24 | Retrofit retrofit = new Retrofit.Builder() 25 | .baseUrl(BASE_URL) 26 | .addConverterFactory(GsonConverterFactory.create()) 27 | .build(); 28 | 29 | @GET("movie/{type}") 30 | Call getMovies(@Path("type") String TYPE, @Query("api_key") String API_KEY, @Query("language") String LANGUAGE, @Query("page") int PAGE); 31 | 32 | @GET("search/movie") 33 | Call searchMovies(@Query("api_key") String API_KEY, @Query("language") String LANGUAGE, @Query("page") int PAGE, @Query("query") String QUERY); 34 | 35 | @GET("movie/{movie_id}/videos") 36 | Call getTrailers(@Path("movie_id") int MOVIE_ID, @Query("api_key") String API_KEY, @Query("language") String LANGUAGE); 37 | 38 | @GET("movie/{movie_id}/reviews") 39 | Call getReviews(@Path("movie_id") int MOVIE_ID, @Query("api_key") String API_KEY, @Query("language") String LANGUAGE); 40 | 41 | @GET("movie/{movie_id}/credits") 42 | Call getCredits(@Path("movie_id") int MOVIE_ID, @Query("api_key") String API_KEY); 43 | 44 | @GET("movie/{movie_id}") 45 | Call getDetails(@Path("movie_id") int MOVIE_ID, @Query("api_key") String API_KEY, @Query("language") String LANGUAGE); 46 | 47 | @GET("movie/{movie_id}/similar") 48 | Call getSimilarMovies(@Path("movie_id") int MOVIE_ID, @Query("api_key") String API_KEY, @Query("language") String LANGUAGE); 49 | 50 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/TMDBResponse.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Created by bapspatil 12 | */ 13 | 14 | public class TMDBResponse implements Parcelable { 15 | public static final Creator CREATOR = new Creator() { 16 | @Override 17 | public TMDBResponse createFromParcel(Parcel source) { 18 | return new TMDBResponse(source); 19 | } 20 | 21 | @Override 22 | public TMDBResponse[] newArray(int size) { 23 | return new TMDBResponse[size]; 24 | } 25 | }; 26 | @SerializedName("page") 27 | private int page; 28 | @SerializedName("results") 29 | private ArrayList results; 30 | @SerializedName("total_results") 31 | private int totalResults; 32 | @SerializedName("total_pages") 33 | private int totalPages; 34 | 35 | public TMDBResponse() { 36 | } 37 | 38 | protected TMDBResponse(Parcel in) { 39 | this.page = in.readInt(); 40 | this.results = in.createTypedArrayList(Movie.CREATOR); 41 | this.totalResults = in.readInt(); 42 | this.totalPages = in.readInt(); 43 | } 44 | 45 | public static Creator getCREATOR() { 46 | return CREATOR; 47 | } 48 | 49 | public int getPage() { 50 | return page; 51 | } 52 | 53 | public void setPage(int page) { 54 | this.page = page; 55 | } 56 | 57 | public ArrayList getResults() { 58 | return results; 59 | } 60 | 61 | public void setResults(ArrayList results) { 62 | this.results = results; 63 | } 64 | 65 | public int getTotalResults() { 66 | return totalResults; 67 | } 68 | 69 | public void setTotalResults(int totalResults) { 70 | this.totalResults = totalResults; 71 | } 72 | 73 | public int getTotalPages() { 74 | return totalPages; 75 | } 76 | 77 | public void setTotalPages(int totalPages) { 78 | this.totalPages = totalPages; 79 | } 80 | 81 | @Override 82 | public int describeContents() { 83 | return 0; 84 | } 85 | 86 | @Override 87 | public void writeToParcel(Parcel dest, int flags) { 88 | dest.writeInt(this.page); 89 | dest.writeTypedList(this.results); 90 | dest.writeInt(this.totalResults); 91 | dest.writeInt(this.totalPages); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/adapters/CastRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import java.util.ArrayList; 13 | 14 | import bapspatil.silverscreener.R; 15 | import bapspatil.silverscreener.model.Cast; 16 | import bapspatil.silverscreener.network.RetrofitAPI; 17 | import bapspatil.silverscreener.utils.GlideApp; 18 | 19 | /** 20 | * Created by bapspatil 21 | */ 22 | 23 | public class CastRecyclerViewAdapter extends RecyclerView.Adapter { 24 | private ArrayList mCastList; 25 | private Context mContext; 26 | private OnActorClickHandler mActorClickHandler; 27 | 28 | public CastRecyclerViewAdapter(Context context, ArrayList cast, OnActorClickHandler onActorClickHandler) { 29 | this.mContext = context; 30 | this.mCastList = cast; 31 | this.mActorClickHandler = onActorClickHandler; 32 | } 33 | 34 | @Override 35 | public CastViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 36 | View view = LayoutInflater.from(mContext).inflate(R.layout.rv_cast, parent, false); 37 | return new CastViewHolder(view); 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(CastViewHolder holder, int position) { 42 | holder.mCastTextView.setText(mCastList.get(position).getName()); 43 | GlideApp.with(mContext) 44 | .load(RetrofitAPI.POSTER_BASE_URL + mCastList.get(position).getProfilePath()) 45 | .into(holder.mCastImageView); 46 | } 47 | 48 | @Override 49 | public int getItemCount() { 50 | return mCastList.size(); 51 | } 52 | 53 | public interface OnActorClickHandler { 54 | void onActorClicked(String actorName); 55 | } 56 | 57 | public class CastViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 58 | public TextView mCastTextView; 59 | public ImageView mCastImageView; 60 | 61 | public CastViewHolder(View itemView) { 62 | super(itemView); 63 | mCastImageView = itemView.findViewById(R.id.cast_iv); 64 | mCastTextView = itemView.findViewById(R.id.cast_tv); 65 | itemView.setOnClickListener(this); 66 | } 67 | 68 | @Override 69 | public void onClick(View view) { 70 | mActorClickHandler.onActorClicked(mCastTextView.getText().toString()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'realm-android' 3 | apply plugin: 'io.fabric' // Fabric's Crashlytics Gradle Plugin 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion '29.0.2' 8 | defaultConfig { 9 | applicationId "bapspatil.silverscreener" 10 | minSdkVersion 21 11 | targetSdkVersion 29 12 | versionCode 63 13 | versionName "one.three.four" 14 | } 15 | buildTypes { 16 | release { 17 | shrinkResources true 18 | minifyEnabled true 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | buildTypes.each { 24 | it.buildConfigField 'String', 'TMDB_API_TOKEN', MyTheMovieDBApiToken 25 | } 26 | 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | } 32 | 33 | repositories { 34 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 35 | } 36 | 37 | ext { 38 | constraintVer = '1.1.3' 39 | glideVer = '4.9.0' 40 | butterknifeVer = '10.1.0' 41 | floatingsearchviewVer = '2.1.1' 42 | retrofitVer = '2.6.0' 43 | gsonVer = '2.8.5' 44 | cookiebar2Ver = '1.1.3' 45 | rvAnimVer = '3.0.0' 46 | expandTextVer = '1.0.5' 47 | } 48 | 49 | dependencies { 50 | implementation fileTree(include: ['*.jar'], dir: 'libs') 51 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 52 | implementation 'androidx.appcompat:appcompat:1.1.0' 53 | implementation 'com.google.android.material:material:1.2.0-alpha01' 54 | implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01' 55 | 56 | implementation "com.github.bumptech.glide:glide:$glideVer" 57 | annotationProcessor "com.github.bumptech.glide:compiler:$glideVer" 58 | implementation "com.jakewharton:butterknife:$butterknifeVer" 59 | annotationProcessor "com.jakewharton:butterknife-compiler:$butterknifeVer" 60 | implementation "com.squareup.retrofit2:retrofit:$retrofitVer" 61 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVer" 62 | implementation "com.google.code.gson:gson:$gsonVer" 63 | implementation "org.aviran.cookiebar2:cookiebar2:$cookiebar2Ver" 64 | implementation "jp.wasabeef:recyclerview-animators:$rvAnimVer" 65 | implementation "at.blogc:expandabletextview:$expandTextVer" 66 | implementation "com.github.arimorty:floatingsearchview:$floatingsearchviewVer" 67 | 68 | implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' // Firebase Crashlytics 69 | implementation "com.google.firebase:firebase-core:17.2.1" // Firebase Analytics 70 | implementation "com.google.firebase:firebase-messaging:20.0.0" // Firebase Cloud Messaging 71 | } 72 | 73 | apply plugin: 'com.google.gms.google-services' // Google Services Gradle Plugin -------------------------------------------------------------------------------- /app/src/main/res/layout/rv_review_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 27 | 28 | 37 | 38 | 39 | 54 | 55 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/adapters/MovieRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.adapters; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 12 | 13 | import java.util.ArrayList; 14 | 15 | import bapspatil.silverscreener.R; 16 | import bapspatil.silverscreener.model.Movie; 17 | import bapspatil.silverscreener.network.RetrofitAPI; 18 | import bapspatil.silverscreener.utils.GlideApp; 19 | import butterknife.BindView; 20 | import butterknife.ButterKnife; 21 | 22 | public class MovieRecyclerViewAdapter extends RecyclerView.Adapter { 23 | 24 | private ArrayList mMoviesArrayList; 25 | private Context mContext; 26 | private ItemClickListener mClickListener; 27 | 28 | public MovieRecyclerViewAdapter(Context context, ArrayList movieArrayList, ItemClickListener itemClickListener) { 29 | this.mContext = context; 30 | this.mMoviesArrayList = movieArrayList; 31 | this.mClickListener = itemClickListener; 32 | } 33 | 34 | @Override 35 | public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 36 | View view = LayoutInflater.from(mContext).inflate(R.layout.rv_movie_item, viewGroup, false); 37 | return new MovieViewHolder(view); 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(MovieViewHolder holder, int position) { 42 | Movie theMovie = mMoviesArrayList.get(position); 43 | if (theMovie.getPosterBytes() != null) { 44 | GlideApp.with(mContext) 45 | .load(theMovie.getPosterBytes()) 46 | .centerCrop() 47 | .error(R.drawable.tmdb_placeholder) 48 | .fallback(R.drawable.tmdb_placeholder) 49 | .transition(new DrawableTransitionOptions().crossFade()) 50 | .into(holder.mPosterImageView); 51 | } else { 52 | GlideApp.with(mContext) 53 | .load(RetrofitAPI.POSTER_BASE_URL + theMovie.getPosterPath()) 54 | .centerCrop() 55 | .error(R.drawable.tmdb_placeholder) 56 | .fallback(R.drawable.tmdb_placeholder) 57 | .transition(new DrawableTransitionOptions().crossFade()) 58 | .into(holder.mPosterImageView); 59 | } 60 | } 61 | 62 | @Override 63 | public int getItemCount() { 64 | if (mMoviesArrayList == null) return 0; 65 | else return mMoviesArrayList.size(); 66 | } 67 | 68 | public interface ItemClickListener { 69 | void onItemClick(int position, ImageView posterImageView); 70 | } 71 | 72 | public class MovieViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 73 | @BindView(R.id.poster_image_view) 74 | ImageView mPosterImageView; 75 | 76 | MovieViewHolder(View itemView) { 77 | super(itemView); 78 | ButterKnife.bind(this, itemView); 79 | itemView.setOnClickListener(this); 80 | } 81 | 82 | @Override 83 | public void onClick(View v) { 84 | if (mClickListener != null) 85 | mClickListener.onItemClick(getAdapterPosition(), mPosterImageView); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/adapters/ReviewRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.view.animation.OvershootInterpolator; 9 | import android.widget.ImageButton; 10 | import android.widget.LinearLayout; 11 | import android.widget.TextView; 12 | 13 | import androidx.core.content.res.ResourcesCompat; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import java.util.ArrayList; 17 | 18 | import at.blogc.android.views.ExpandableTextView; 19 | import bapspatil.silverscreener.R; 20 | import butterknife.BindView; 21 | import butterknife.ButterKnife; 22 | 23 | public class ReviewRecyclerViewAdapter extends RecyclerView.Adapter { 24 | 25 | private ArrayList mReviewAuthors, mReviewContents; 26 | private Context mContext; 27 | 28 | public ReviewRecyclerViewAdapter(Context context, ArrayList reviewAuthors, ArrayList reviewContents) { 29 | this.mContext = context; 30 | this.mReviewAuthors = reviewAuthors; 31 | this.mReviewContents = reviewContents; 32 | } 33 | 34 | @Override 35 | public ReviewItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 36 | View view = LayoutInflater.from(mContext).inflate(R.layout.rv_review_item, parent, false); 37 | return new ReviewItemViewHolder(view); 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(final ReviewItemViewHolder holder, int position) { 42 | String reviewAuthor = mReviewAuthors.get(position); 43 | Typeface hammersmithOne = ResourcesCompat.getFont(mContext, R.font.hammersmith_one); 44 | holder.reviewAuthorTextView.setTypeface(hammersmithOne); 45 | holder.reviewContentTextView.setTypeface(hammersmithOne); 46 | holder.reviewAuthorTextView.setText(reviewAuthor); 47 | holder.reviewContentTextView.setInterpolator(new OvershootInterpolator()); 48 | holder.reviewContentTextView.setText(mReviewContents.get(position)); 49 | holder.reviewExpandButton.setOnClickListener(view -> { 50 | holder.reviewExpandButton.setImageResource(holder.reviewContentTextView.isExpanded() ? R.drawable.ic_expand_more_white_24dp : R.drawable.ic_expand_less_white_24dp); 51 | holder.reviewContentTextView.toggle(); 52 | }); 53 | holder.reviewLinearLayout.setOnClickListener(view -> { 54 | holder.reviewExpandButton.setImageResource(holder.reviewContentTextView.isExpanded() ? R.drawable.ic_expand_more_white_24dp : R.drawable.ic_expand_less_white_24dp); 55 | holder.reviewContentTextView.toggle(); 56 | }); 57 | } 58 | 59 | @Override 60 | public int getItemCount() { 61 | if (mReviewAuthors == null) return 0; 62 | else return mReviewAuthors.size(); 63 | } 64 | 65 | public class ReviewItemViewHolder extends RecyclerView.ViewHolder { 66 | @BindView(R.id.review_author_tv) 67 | TextView reviewAuthorTextView; 68 | @BindView(R.id.review_content_tv) 69 | ExpandableTextView reviewContentTextView; 70 | @BindView(R.id.review_expand_button) 71 | ImageButton reviewExpandButton; 72 | @BindView(R.id.review_linear_layout) 73 | LinearLayout reviewLinearLayout; 74 | 75 | ReviewItemViewHolder(View itemView) { 76 | super(itemView); 77 | ButterKnife.bind(this, itemView); 78 | 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/adapters/TrailerRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import androidx.core.content.res.ResourcesCompat; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 15 | 16 | import java.util.ArrayList; 17 | 18 | import bapspatil.silverscreener.R; 19 | import bapspatil.silverscreener.utils.GlideApp; 20 | import butterknife.BindView; 21 | import butterknife.ButterKnife; 22 | 23 | public class TrailerRecyclerViewAdapter extends RecyclerView.Adapter { 24 | 25 | private ArrayList mTrailerTitles, mTrailerPaths; 26 | private Context mContext; 27 | private ItemClickListener mClickListener; 28 | 29 | public TrailerRecyclerViewAdapter(Context context, ArrayList trailerTitles, ArrayList trailerPaths, ItemClickListener itemClickListener) { 30 | this.mContext = context; 31 | this.mClickListener = itemClickListener; 32 | this.mTrailerTitles = trailerTitles; 33 | this.mTrailerPaths = trailerPaths; 34 | } 35 | 36 | @Override 37 | public TrailerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 38 | View view = LayoutInflater.from(mContext).inflate(R.layout.rv_trailer_item, viewGroup, false); 39 | return new TrailerViewHolder(view); 40 | } 41 | 42 | @Override 43 | public void onBindViewHolder(TrailerViewHolder viewHolder, int position) { 44 | String title = mTrailerTitles.get(position); 45 | Typeface hammersmithOne = ResourcesCompat.getFont(mContext, R.font.hammersmith_one); 46 | viewHolder.trailerTitleTextView.setTypeface(hammersmithOne); 47 | if (title == null) 48 | viewHolder.trailerTitleTextView.setText("Why aren't you connected to the internet? Or maybe there are no trailers for this movie..."); 49 | else 50 | viewHolder.trailerTitleTextView.setText(title); 51 | 52 | String thumbnailUrlStr = "https://img.youtube.com/vi/" + mTrailerPaths.get(position) + "/0.jpg"; 53 | GlideApp.with(mContext) 54 | .load(thumbnailUrlStr) 55 | .error(R.drawable.cursor_search) 56 | .fallback(R.drawable.cursor_search) 57 | .centerCrop() 58 | .transition(new DrawableTransitionOptions().crossFade()) 59 | .into(viewHolder.trailerThumbnailImageView); 60 | } 61 | 62 | @Override 63 | public int getItemCount() { 64 | if (mTrailerTitles == null) return 0; 65 | else return mTrailerTitles.size(); 66 | } 67 | 68 | public interface ItemClickListener { 69 | void onItemClick(String stringUrlTrailerClicked); 70 | } 71 | 72 | public class TrailerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 73 | @BindView(R.id.trailer_title_tv) 74 | TextView trailerTitleTextView; 75 | @BindView(R.id.trailer_thumbnail_iv) 76 | ImageView trailerThumbnailImageView; 77 | 78 | TrailerViewHolder(View itemView) { 79 | super(itemView); 80 | ButterKnife.bind(this, itemView); 81 | itemView.setOnClickListener(this); 82 | } 83 | 84 | @Override 85 | public void onClick(View v) { 86 | if (mClickListener != null) 87 | mClickListener.onItemClick(mTrailerPaths.get(getAdapterPosition())); 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about_me.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 23 | 24 | 33 | 34 | 35 | 36 | 43 | 51 | 52 | 61 | 62 | 68 | 69 | 75 | 76 | 82 | 83 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/Movie.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import io.realm.RealmObject; 9 | import io.realm.annotations.PrimaryKey; 10 | 11 | public class Movie extends RealmObject implements Parcelable { 12 | 13 | public static final Creator CREATOR = new Creator() { 14 | @Override 15 | public Movie createFromParcel(Parcel source) { 16 | return new Movie(source); 17 | } 18 | 19 | @Override 20 | public Movie[] newArray(int size) { 21 | return new Movie[size]; 22 | } 23 | }; 24 | @PrimaryKey 25 | @SerializedName("id") 26 | private int id; 27 | @SerializedName("poster_path") 28 | private String posterPath; 29 | @SerializedName("title") 30 | private String title; 31 | @SerializedName("overview") 32 | private String plot; 33 | @SerializedName("release_date") 34 | private String date; 35 | @SerializedName("vote_average") 36 | private String rating; 37 | @SerializedName("backdrop_path") 38 | private String backdropPath; 39 | private byte[] posterBytes; 40 | 41 | public Movie() { 42 | } 43 | 44 | public Movie(String posterPath, String title, String plot, String date, String rating, String backdropPath, int id, byte[] posterBytes) { 45 | 46 | this.posterPath = posterPath; 47 | this.title = title; 48 | this.plot = plot; 49 | this.date = date; 50 | this.rating = rating; 51 | this.backdropPath = backdropPath; 52 | this.id = id; 53 | this.posterBytes = posterBytes; 54 | } 55 | 56 | protected Movie(Parcel in) { 57 | this.posterPath = in.readString(); 58 | this.title = in.readString(); 59 | this.plot = in.readString(); 60 | this.date = in.readString(); 61 | this.rating = in.readString(); 62 | this.backdropPath = in.readString(); 63 | this.id = in.readInt(); 64 | this.posterBytes = in.createByteArray(); 65 | } 66 | 67 | public String getPosterPath() { 68 | 69 | return posterPath; 70 | } 71 | 72 | public void setPosterPath(String posterPath) { 73 | this.posterPath = posterPath; 74 | } 75 | 76 | public String getTitle() { 77 | return title; 78 | } 79 | 80 | public void setTitle(String title) { 81 | this.title = title; 82 | } 83 | 84 | public String getPlot() { 85 | return plot; 86 | } 87 | 88 | public void setPlot(String plot) { 89 | this.plot = plot; 90 | } 91 | 92 | public String getDate() { 93 | return date; 94 | } 95 | 96 | public void setDate(String date) { 97 | this.date = date; 98 | } 99 | 100 | public String getRating() { 101 | return rating; 102 | } 103 | 104 | public void setRating(String rating) { 105 | this.rating = rating; 106 | } 107 | 108 | public String getBackdropPath() { 109 | return backdropPath; 110 | } 111 | 112 | public void setBackdropPath(String backdropPath) { 113 | this.backdropPath = backdropPath; 114 | } 115 | 116 | public int getId() { 117 | return id; 118 | } 119 | 120 | public void setId(int id) { 121 | this.id = id; 122 | } 123 | 124 | public byte[] getPosterBytes() { 125 | return posterBytes; 126 | } 127 | 128 | public void setPosterBytes(byte[] posterBytes) { 129 | this.posterBytes = posterBytes; 130 | } 131 | 132 | @Override 133 | public int describeContents() { 134 | return 0; 135 | } 136 | 137 | @Override 138 | public void writeToParcel(Parcel dest, int flags) { 139 | dest.writeString(this.posterPath); 140 | dest.writeString(this.title); 141 | dest.writeString(this.plot); 142 | dest.writeString(this.date); 143 | dest.writeString(this.rating); 144 | dest.writeString(this.backdropPath); 145 | dest.writeInt(this.id); 146 | dest.writeByteArray(this.posterBytes); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 17 | 18 | 27 | 28 | 43 | 44 | 45 | 46 | 52 | 53 | 57 | 58 | 66 | 67 | 74 | 75 | 76 | 80 | 81 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/utils/EndlessScrollListener.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.utils; 2 | 3 | import androidx.recyclerview.widget.GridLayoutManager; 4 | import androidx.recyclerview.widget.LinearLayoutManager; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager; 7 | 8 | /* 9 | ** Created by bapspatil 10 | */ 11 | 12 | public abstract class EndlessScrollListener extends RecyclerView.OnScrollListener { 13 | 14 | RecyclerView.LayoutManager mLayoutManager; 15 | // The minimum amount of items to have below your current scroll position 16 | // before loading more. 17 | private int visibleThreshold = 5; 18 | // The current offset index of data you have loaded 19 | private int currentPage = 1; 20 | // The total number of items in the dataset after the last load 21 | private int previousTotalItemCount = 0; 22 | // True if we are still waiting for the last set of data to load. 23 | private boolean loading = true; 24 | // Sets the starting page index 25 | private int startingPageIndex = 0; 26 | 27 | public EndlessScrollListener(LinearLayoutManager layoutManager) { 28 | this.mLayoutManager = layoutManager; 29 | } 30 | 31 | public EndlessScrollListener(GridLayoutManager layoutManager) { 32 | this.mLayoutManager = layoutManager; 33 | visibleThreshold = visibleThreshold * layoutManager.getSpanCount(); 34 | } 35 | 36 | public EndlessScrollListener(StaggeredGridLayoutManager layoutManager) { 37 | this.mLayoutManager = layoutManager; 38 | visibleThreshold = visibleThreshold * layoutManager.getSpanCount(); 39 | } 40 | 41 | public int getLastVisibleItem(int[] lastVisibleItemPositions) { 42 | int maxSize = 0; 43 | for (int i = 0; i < lastVisibleItemPositions.length; i++) { 44 | if (i == 0) { 45 | maxSize = lastVisibleItemPositions[i]; 46 | } else if (lastVisibleItemPositions[i] > maxSize) { 47 | maxSize = lastVisibleItemPositions[i]; 48 | } 49 | } 50 | return maxSize; 51 | } 52 | 53 | // This happens many times a second during a scroll, so be wary of the code you place here. 54 | // We are given a few useful parameters to help us work out if we need to load some more data, 55 | // but first we check if we are waiting for the previous load to finish. 56 | @Override 57 | public void onScrolled(RecyclerView view, int dx, int dy) { 58 | int lastVisibleItemPosition = 0; 59 | int totalItemCount = mLayoutManager.getItemCount(); 60 | 61 | if (mLayoutManager instanceof StaggeredGridLayoutManager) { 62 | int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null); 63 | // get maximum element within the list 64 | lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions); 65 | } else if (mLayoutManager instanceof LinearLayoutManager) { 66 | lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); 67 | } else if (mLayoutManager instanceof GridLayoutManager) { 68 | lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition(); 69 | } 70 | 71 | // If the total item count is zero and the previous isn't, assume the 72 | // list is invalidated and should be reset back to initial state 73 | if (totalItemCount < previousTotalItemCount) { 74 | this.currentPage = this.startingPageIndex; 75 | this.previousTotalItemCount = totalItemCount; 76 | if (totalItemCount == 0) { 77 | this.loading = true; 78 | } 79 | } 80 | // If it’s still loading, we check to see if the dataset count has 81 | // changed, if so we conclude it has finished loading and update the current page 82 | // number and total item count. 83 | if (loading && (totalItemCount > previousTotalItemCount)) { 84 | loading = false; 85 | previousTotalItemCount = totalItemCount; 86 | } 87 | 88 | // If it isn’t currently loading, we check to see if we have breached 89 | // the visibleThreshold and need to reload more data. 90 | // If we do need to reload some more data, we execute onLoadMore to fetch the data. 91 | // threshold should reflect how many total columns there are too 92 | if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) { 93 | currentPage++; 94 | onLoadMore(currentPage, totalItemCount); 95 | loading = true; 96 | } 97 | } 98 | 99 | // Defines the process for actually loading more data based on page 100 | public abstract void onLoadMore(int page, int totalItemsCount); 101 | 102 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/model/TMDBDetailsResponse.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | import java.util.List; 9 | 10 | public class TMDBDetailsResponse implements Parcelable { 11 | 12 | public static final Creator CREATOR = new Creator() { 13 | @Override 14 | public TMDBDetailsResponse createFromParcel(Parcel source) { 15 | return new TMDBDetailsResponse(source); 16 | } 17 | 18 | @Override 19 | public TMDBDetailsResponse[] newArray(int size) { 20 | return new TMDBDetailsResponse[size]; 21 | } 22 | }; 23 | @SerializedName("original_language") 24 | private String originalLanguage; 25 | @SerializedName("imdb_id") 26 | private String imdbId; 27 | @SerializedName("video") 28 | private boolean video; 29 | @SerializedName("title") 30 | private String title; 31 | @SerializedName("backdrop_path") 32 | private String backdropPath; 33 | @SerializedName("revenue") 34 | private int revenue; 35 | @SerializedName("genres") 36 | private List genres; 37 | @SerializedName("popularity") 38 | private double popularity; 39 | @SerializedName("id") 40 | private int id; 41 | @SerializedName("vote_count") 42 | private int voteCount; 43 | @SerializedName("budget") 44 | private int budget; 45 | @SerializedName("overview") 46 | private String overview; 47 | @SerializedName("original_title") 48 | private String originalTitle; 49 | @SerializedName("runtime") 50 | private int runtime; 51 | @SerializedName("poster_path") 52 | private String posterPath; 53 | @SerializedName("release_date") 54 | private String releaseDate; 55 | @SerializedName("vote_average") 56 | private double voteAverage; 57 | @SerializedName("tagline") 58 | private String tagline; 59 | @SerializedName("adult") 60 | private boolean adult; 61 | @SerializedName("homepage") 62 | private String homepage; 63 | @SerializedName("status") 64 | private String status; 65 | 66 | public TMDBDetailsResponse() { 67 | } 68 | 69 | public TMDBDetailsResponse(String originalLanguage, String imdbId, boolean video, String title, String backdropPath, int revenue, List genres, double popularity, int id, int voteCount, int budget, String overview, String originalTitle, int runtime, String posterPath, String releaseDate, double voteAverage, String tagline, boolean adult, String homepage, String status) { 70 | this.originalLanguage = originalLanguage; 71 | this.imdbId = imdbId; 72 | this.video = video; 73 | this.title = title; 74 | 75 | this.backdropPath = backdropPath; 76 | this.revenue = revenue; 77 | this.genres = genres; 78 | this.popularity = popularity; 79 | this.id = id; 80 | this.voteCount = voteCount; 81 | this.budget = budget; 82 | this.overview = overview; 83 | this.originalTitle = originalTitle; 84 | this.runtime = runtime; 85 | this.posterPath = posterPath; 86 | this.releaseDate = releaseDate; 87 | this.voteAverage = voteAverage; 88 | this.tagline = tagline; 89 | this.adult = adult; 90 | this.homepage = homepage; 91 | this.status = status; 92 | } 93 | 94 | protected TMDBDetailsResponse(Parcel in) { 95 | this.originalLanguage = in.readString(); 96 | this.imdbId = in.readString(); 97 | this.video = in.readByte() != 0; 98 | this.title = in.readString(); 99 | this.backdropPath = in.readString(); 100 | this.revenue = in.readInt(); 101 | this.genres = in.createTypedArrayList(GenresItem.CREATOR); 102 | this.popularity = in.readDouble(); 103 | this.id = in.readInt(); 104 | this.voteCount = in.readInt(); 105 | this.budget = in.readInt(); 106 | this.overview = in.readString(); 107 | this.originalTitle = in.readString(); 108 | this.runtime = in.readInt(); 109 | this.posterPath = in.readString(); 110 | this.releaseDate = in.readString(); 111 | this.voteAverage = in.readDouble(); 112 | this.tagline = in.readString(); 113 | this.adult = in.readByte() != 0; 114 | this.homepage = in.readString(); 115 | this.status = in.readString(); 116 | } 117 | 118 | public String getOriginalLanguage() { 119 | return originalLanguage; 120 | } 121 | 122 | public void setOriginalLanguage(String originalLanguage) { 123 | this.originalLanguage = originalLanguage; 124 | } 125 | 126 | public String getImdbId() { 127 | return imdbId; 128 | } 129 | 130 | public void setImdbId(String imdbId) { 131 | this.imdbId = imdbId; 132 | } 133 | 134 | public boolean isVideo() { 135 | return video; 136 | } 137 | 138 | public void setVideo(boolean video) { 139 | this.video = video; 140 | } 141 | 142 | public String getTitle() { 143 | return title; 144 | } 145 | 146 | public void setTitle(String title) { 147 | this.title = title; 148 | } 149 | 150 | public String getBackdropPath() { 151 | return backdropPath; 152 | } 153 | 154 | public void setBackdropPath(String backdropPath) { 155 | this.backdropPath = backdropPath; 156 | } 157 | 158 | public int getRevenue() { 159 | return revenue; 160 | } 161 | 162 | public void setRevenue(int revenue) { 163 | this.revenue = revenue; 164 | } 165 | 166 | public List getGenres() { 167 | return genres; 168 | } 169 | 170 | public void setGenres(List genres) { 171 | this.genres = genres; 172 | } 173 | 174 | public double getPopularity() { 175 | return popularity; 176 | } 177 | 178 | public void setPopularity(double popularity) { 179 | this.popularity = popularity; 180 | } 181 | 182 | public int getId() { 183 | return id; 184 | } 185 | 186 | public void setId(int id) { 187 | this.id = id; 188 | } 189 | 190 | public int getVoteCount() { 191 | return voteCount; 192 | } 193 | 194 | public void setVoteCount(int voteCount) { 195 | this.voteCount = voteCount; 196 | } 197 | 198 | public int getBudget() { 199 | return budget; 200 | } 201 | 202 | public void setBudget(int budget) { 203 | this.budget = budget; 204 | } 205 | 206 | public String getOverview() { 207 | return overview; 208 | } 209 | 210 | public void setOverview(String overview) { 211 | this.overview = overview; 212 | } 213 | 214 | public String getOriginalTitle() { 215 | return originalTitle; 216 | } 217 | 218 | public void setOriginalTitle(String originalTitle) { 219 | this.originalTitle = originalTitle; 220 | } 221 | 222 | public int getRuntime() { 223 | return runtime; 224 | } 225 | 226 | public void setRuntime(int runtime) { 227 | this.runtime = runtime; 228 | } 229 | 230 | public String getPosterPath() { 231 | return posterPath; 232 | } 233 | 234 | public void setPosterPath(String posterPath) { 235 | this.posterPath = posterPath; 236 | } 237 | 238 | public String getReleaseDate() { 239 | return releaseDate; 240 | } 241 | 242 | public void setReleaseDate(String releaseDate) { 243 | this.releaseDate = releaseDate; 244 | } 245 | 246 | public double getVoteAverage() { 247 | return voteAverage; 248 | } 249 | 250 | public void setVoteAverage(double voteAverage) { 251 | this.voteAverage = voteAverage; 252 | } 253 | 254 | public String getTagline() { 255 | return tagline; 256 | } 257 | 258 | public void setTagline(String tagline) { 259 | this.tagline = tagline; 260 | } 261 | 262 | public boolean isAdult() { 263 | return adult; 264 | } 265 | 266 | public void setAdult(boolean adult) { 267 | this.adult = adult; 268 | } 269 | 270 | public String getHomepage() { 271 | return homepage; 272 | } 273 | 274 | public void setHomepage(String homepage) { 275 | this.homepage = homepage; 276 | } 277 | 278 | public String getStatus() { 279 | return status; 280 | } 281 | 282 | public void setStatus(String status) { 283 | this.status = status; 284 | } 285 | 286 | @Override 287 | public int describeContents() { 288 | return 0; 289 | } 290 | 291 | @Override 292 | public void writeToParcel(Parcel dest, int flags) { 293 | dest.writeString(this.originalLanguage); 294 | dest.writeString(this.imdbId); 295 | dest.writeByte(this.video ? (byte) 1 : (byte) 0); 296 | dest.writeString(this.title); 297 | dest.writeString(this.backdropPath); 298 | dest.writeInt(this.revenue); 299 | dest.writeTypedList(this.genres); 300 | dest.writeDouble(this.popularity); 301 | dest.writeInt(this.id); 302 | dest.writeInt(this.voteCount); 303 | dest.writeInt(this.budget); 304 | dest.writeString(this.overview); 305 | dest.writeString(this.originalTitle); 306 | dest.writeInt(this.runtime); 307 | dest.writeString(this.posterPath); 308 | dest.writeString(this.releaseDate); 309 | dest.writeDouble(this.voteAverage); 310 | dest.writeString(this.tagline); 311 | dest.writeByte(this.adult ? (byte) 1 : (byte) 0); 312 | dest.writeString(this.homepage); 313 | dest.writeString(this.status); 314 | } 315 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.ui; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.res.Configuration; 7 | import android.graphics.Color; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.speech.RecognizerIntent; 11 | import android.transition.Slide; 12 | import android.view.Gravity; 13 | import android.view.View; 14 | import android.widget.ImageView; 15 | import android.widget.ProgressBar; 16 | import android.widget.Toast; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.appcompat.app.AppCompatActivity; 20 | import androidx.core.app.ActivityOptionsCompat; 21 | import androidx.core.content.ContextCompat; 22 | import androidx.recyclerview.widget.GridLayoutManager; 23 | import androidx.recyclerview.widget.RecyclerView; 24 | 25 | import com.arlib.floatingsearchview.FloatingSearchView; 26 | import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion; 27 | import com.google.android.material.bottomnavigation.BottomNavigationView; 28 | 29 | import org.aviran.cookiebar2.CookieBar; 30 | 31 | import java.util.ArrayList; 32 | 33 | import bapspatil.silverscreener.BuildConfig; 34 | import bapspatil.silverscreener.R; 35 | import bapspatil.silverscreener.adapters.MovieRecyclerViewAdapter; 36 | import bapspatil.silverscreener.data.RealmDataSource; 37 | import bapspatil.silverscreener.model.Movie; 38 | import bapspatil.silverscreener.model.MovieRecyclerView; 39 | import bapspatil.silverscreener.model.TMDBResponse; 40 | import bapspatil.silverscreener.network.RetrofitAPI; 41 | import bapspatil.silverscreener.utils.NetworkUtils; 42 | import butterknife.BindView; 43 | import butterknife.ButterKnife; 44 | import jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter; 45 | import retrofit2.Call; 46 | import retrofit2.Callback; 47 | import retrofit2.Response; 48 | 49 | public class MainActivity extends AppCompatActivity implements MovieRecyclerViewAdapter.ItemClickListener { 50 | private static final int SEARCH_TASK = 0, POPULAR_TASK = 1, TOP_RATED_TASK = 2, UPCOMING_TASK = 3, NOW_PLAYING_TASK = 4; 51 | private final int VOICE_RECOGNITION_REQUEST_CODE = 13; 52 | @BindView(R.id.loading_indicator) 53 | ProgressBar mProgressBar; 54 | @BindView(R.id.rv_movies) 55 | MovieRecyclerView mRecyclerView; 56 | @BindView(R.id.bottom_navigation) 57 | BottomNavigationView bottomNavigationView; 58 | @BindView(R.id.search_view) 59 | FloatingSearchView searchView; 60 | private MovieRecyclerViewAdapter mAdapter; 61 | private ArrayList movieArray = new ArrayList<>(); 62 | private Context mContext; 63 | private RealmDataSource dataSource; 64 | 65 | @Override 66 | protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.activity_main); 69 | ButterKnife.bind(this); 70 | getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimary)); 71 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 72 | getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.colorPrimary)); 73 | } 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 75 | Slide slide = new Slide(Gravity.LEFT); 76 | getWindow().setExitTransition(slide); 77 | } 78 | mContext = getApplicationContext(); 79 | CookieBar.build(MainActivity.this) 80 | .setLayoutGravity(Gravity.BOTTOM) 81 | .setBackgroundColor(R.color.colorAccent) 82 | .setTitleColor(R.color.colorPrimary) 83 | .setTitle("App developed by Bapusaheb Patil") 84 | .show(); 85 | 86 | dataSource = new RealmDataSource(); 87 | dataSource.open(); 88 | 89 | int columns = 2; 90 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) 91 | columns = 4; 92 | GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, columns); 93 | mRecyclerView.setLayoutManager(gridLayoutManager); 94 | 95 | mAdapter = new MovieRecyclerViewAdapter(mContext, movieArray, this); 96 | mRecyclerView.setAdapter(new ScaleInAnimationAdapter(mAdapter)); 97 | 98 | fetchMovies(POPULAR_TASK, null); 99 | 100 | bottomNavigationView.setOnNavigationItemSelectedListener(item -> { 101 | switch (item.getItemId()) { 102 | case R.id.action_popular: 103 | mRecyclerView.smoothScrollToPosition(0); 104 | fetchMovies(POPULAR_TASK, null); 105 | break; 106 | case R.id.action_rated: 107 | mRecyclerView.smoothScrollToPosition(0); 108 | fetchMovies(TOP_RATED_TASK, null); 109 | break; 110 | case R.id.action_upcoming: 111 | mRecyclerView.smoothScrollToPosition(0); 112 | fetchMovies(UPCOMING_TASK, null); 113 | break; 114 | case R.id.action_now: 115 | mRecyclerView.smoothScrollToPosition(0); 116 | fetchMovies(NOW_PLAYING_TASK, null); 117 | break; 118 | case R.id.action_favorites: 119 | mRecyclerView.smoothScrollToPosition(0); 120 | fetchFavs(); 121 | break; 122 | default: 123 | fetchMovies(POPULAR_TASK, null); 124 | } 125 | return true; 126 | }); 127 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 128 | @Override 129 | public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { 130 | super.onScrolled(recyclerView, dx, dy); 131 | if (dy > 0) { // Scrolled up 132 | bottomNavigationView.setVisibility(View.GONE); 133 | } else { 134 | bottomNavigationView.setVisibility(View.VISIBLE); 135 | } 136 | } 137 | }); 138 | 139 | searchView = findViewById(R.id.search_view); 140 | searchView.setOnSearchListener(new FloatingSearchView.OnSearchListener() { 141 | @Override 142 | public void onSuggestionClicked(SearchSuggestion searchSuggestion) { 143 | 144 | } 145 | 146 | @Override 147 | public void onSearchAction(String currentQuery) { 148 | mRecyclerView.smoothScrollToPosition(0); 149 | fetchMovies(SEARCH_TASK, currentQuery); 150 | searchView.clearQuery(); 151 | } 152 | }); 153 | searchView.setOnMenuItemClickListener(item -> { 154 | switch (item.getItemId()) { 155 | case R.id.action_about_me: 156 | Intent intentToAboutMe = new Intent(this, AboutMeActivity.class); 157 | ActivityOptionsCompat options = ActivityOptionsCompat.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out); 158 | startActivity(intentToAboutMe, options.toBundle()); 159 | break; 160 | case R.id.action_voice_search: 161 | startVoiceRecognition(); 162 | } 163 | }); 164 | } 165 | 166 | private void fetchMovies(int taskId, String taskQuery) { 167 | mRecyclerView.setVisibility(View.INVISIBLE); 168 | mProgressBar.setVisibility(View.VISIBLE); 169 | RetrofitAPI retrofitAPI = NetworkUtils.getCacheEnabledRetrofit(getApplicationContext()).create(RetrofitAPI.class); 170 | Call call; 171 | switch (taskId) { 172 | case SEARCH_TASK: 173 | call = retrofitAPI.searchMovies(BuildConfig.TMDB_API_TOKEN, "en-US", 1, taskQuery); 174 | break; 175 | case POPULAR_TASK: 176 | call = retrofitAPI.getMovies("popular", BuildConfig.TMDB_API_TOKEN, "en-US", 1); 177 | break; 178 | case TOP_RATED_TASK: 179 | call = retrofitAPI.getMovies("top_rated", BuildConfig.TMDB_API_TOKEN, "en-US", 1); 180 | break; 181 | case UPCOMING_TASK: 182 | call = retrofitAPI.getMovies("upcoming", BuildConfig.TMDB_API_TOKEN, "en-US", 1); 183 | break; 184 | case NOW_PLAYING_TASK: 185 | call = retrofitAPI.getMovies("now_playing", BuildConfig.TMDB_API_TOKEN, "en-US", 1); 186 | break; 187 | default: 188 | call = retrofitAPI.getMovies("popular", BuildConfig.TMDB_API_TOKEN, "en-US", 1); 189 | } 190 | call.enqueue(new Callback() { 191 | @Override 192 | public void onResponse(Call call, Response response) { 193 | TMDBResponse tmdbResponse = response.body(); 194 | movieArray.clear(); 195 | if (tmdbResponse != null) { 196 | movieArray.addAll(tmdbResponse.getResults()); 197 | mAdapter.notifyDataSetChanged(); 198 | } 199 | mRecyclerView.setVisibility(View.VISIBLE); 200 | mProgressBar.setVisibility(View.INVISIBLE); 201 | } 202 | 203 | @Override 204 | public void onFailure(Call call, Throwable t) { 205 | Toast.makeText(mContext, "Error!", Toast.LENGTH_LONG).show(); 206 | mRecyclerView.setVisibility(View.INVISIBLE); 207 | mProgressBar.setVisibility(View.GONE); 208 | } 209 | }); 210 | } 211 | 212 | private void fetchFavs() { 213 | mRecyclerView.setVisibility(View.INVISIBLE); 214 | mProgressBar.setVisibility(View.VISIBLE); 215 | movieArray.clear(); 216 | movieArray.addAll(dataSource.getAllFavMovies()); 217 | mAdapter.notifyDataSetChanged(); 218 | mRecyclerView.setVisibility(View.VISIBLE); 219 | mProgressBar.setVisibility(View.INVISIBLE); 220 | } 221 | 222 | @Override 223 | public void onItemClick(int position, ImageView posterImageView) { 224 | Movie movie; 225 | movie = movieArray.get(position); 226 | Intent startDetailsActivity = new Intent(mContext, DetailsActivity.class); 227 | startDetailsActivity.putExtra("movie", movie); 228 | ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, posterImageView, "posterTransition"); 229 | startActivity(startDetailsActivity, options.toBundle()); 230 | } 231 | 232 | private void startVoiceRecognition() { 233 | Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 234 | intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); 235 | intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Voice searching..."); 236 | startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE); 237 | } 238 | 239 | @Override 240 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 241 | if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == Activity.RESULT_OK) { 242 | ArrayList matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); 243 | if (matches != null) { 244 | if (!matches.isEmpty()) { 245 | String query = matches.get(0); 246 | fetchMovies(SEARCH_TASK, query); 247 | Toast.makeText(this, "Searching for " + query + "...", Toast.LENGTH_LONG).show(); 248 | } 249 | } 250 | } 251 | super.onActivityResult(requestCode, resultCode, data); 252 | } 253 | 254 | @Override 255 | public void onBackPressed() { 256 | if (searchView.isSearchBarFocused()) 257 | searchView.clearQuery(); 258 | else 259 | super.onBackPressed(); 260 | } 261 | 262 | @Override 263 | protected void onDestroy() { 264 | super.onDestroy(); 265 | dataSource.close(); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 18 | 27 | 28 | 36 | 37 | 42 | 43 | 44 | 45 | 51 | 52 | 61 | 62 | 66 | 67 | 79 | 80 | 91 | 92 | 101 | 102 | 107 | 108 | 115 | 116 | 124 | 125 | 126 | 127 | 132 | 133 | 140 | 141 | 150 | 151 | 152 | 157 | 158 | 165 | 166 | 175 | 176 | 177 | 178 | 186 | 187 | 192 | 193 | 200 | 201 | 209 | 210 | 211 | 216 | 217 | 224 | 225 | 233 | 234 | 235 | 240 | 241 | 248 | 249 | 257 | 258 | 259 | 260 | 272 | 273 | 281 | 282 | 288 | 289 | 299 | 300 | 308 | 309 | 314 | 315 | 319 | 320 | 321 | 322 | 331 | 332 | 337 | 338 | 343 | 344 | 345 | 346 | 357 | 358 | 364 | 365 | 368 | 369 | 375 | 376 | 377 | 386 | 387 | 393 | 394 | 395 | 396 | 397 | 407 | 417 | 418 | 425 | 426 | 427 | -------------------------------------------------------------------------------- /app/src/main/java/bapspatil/silverscreener/ui/DetailsActivity.java: -------------------------------------------------------------------------------- 1 | package bapspatil.silverscreener.ui; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.graphics.drawable.Drawable; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.transition.Slide; 12 | import android.view.Gravity; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.ImageButton; 16 | import android.widget.ImageView; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import androidx.annotation.Nullable; 21 | import androidx.appcompat.app.AppCompatActivity; 22 | import androidx.appcompat.widget.Toolbar; 23 | import androidx.core.app.NavUtils; 24 | import androidx.core.content.ContextCompat; 25 | import androidx.recyclerview.widget.LinearLayoutManager; 26 | import androidx.recyclerview.widget.RecyclerView; 27 | 28 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 29 | import com.bumptech.glide.request.Request; 30 | import com.bumptech.glide.request.target.SizeReadyCallback; 31 | import com.bumptech.glide.request.target.Target; 32 | import com.bumptech.glide.request.transition.Transition; 33 | import com.google.android.material.appbar.AppBarLayout; 34 | import com.google.android.material.appbar.CollapsingToolbarLayout; 35 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 36 | 37 | import org.aviran.cookiebar2.CookieBar; 38 | 39 | import java.io.ByteArrayOutputStream; 40 | import java.text.DateFormat; 41 | import java.text.ParseException; 42 | import java.text.SimpleDateFormat; 43 | import java.util.ArrayList; 44 | import java.util.Date; 45 | 46 | import bapspatil.silverscreener.BuildConfig; 47 | import bapspatil.silverscreener.R; 48 | import bapspatil.silverscreener.adapters.CastRecyclerViewAdapter; 49 | import bapspatil.silverscreener.adapters.GenresRecyclerViewAdapter; 50 | import bapspatil.silverscreener.adapters.MovieRecyclerViewAdapter; 51 | import bapspatil.silverscreener.adapters.ReviewRecyclerViewAdapter; 52 | import bapspatil.silverscreener.adapters.TrailerRecyclerViewAdapter; 53 | import bapspatil.silverscreener.data.RealmDataSource; 54 | import bapspatil.silverscreener.model.Cast; 55 | import bapspatil.silverscreener.model.Crew; 56 | import bapspatil.silverscreener.model.GenresItem; 57 | import bapspatil.silverscreener.model.Movie; 58 | import bapspatil.silverscreener.model.MovieRecyclerView; 59 | import bapspatil.silverscreener.model.Review; 60 | import bapspatil.silverscreener.model.TMDBCreditsResponse; 61 | import bapspatil.silverscreener.model.TMDBDetailsResponse; 62 | import bapspatil.silverscreener.model.TMDBResponse; 63 | import bapspatil.silverscreener.model.TMDBReviewResponse; 64 | import bapspatil.silverscreener.model.TMDBTrailerResponse; 65 | import bapspatil.silverscreener.model.Trailer; 66 | import bapspatil.silverscreener.network.RetrofitAPI; 67 | import bapspatil.silverscreener.utils.GlideApp; 68 | import bapspatil.silverscreener.utils.NetworkUtils; 69 | import butterknife.BindView; 70 | import butterknife.ButterKnife; 71 | import jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter; 72 | import retrofit2.Call; 73 | import retrofit2.Callback; 74 | import retrofit2.Response; 75 | 76 | public class DetailsActivity extends AppCompatActivity implements TrailerRecyclerViewAdapter.ItemClickListener { 77 | private static final int TRAILERS_DETAILS_TYPE = 0, REVIEWS_DETAILS_TYPE = 1; 78 | Movie tempMovie, mMovie; 79 | @BindView(R.id.appbar) 80 | AppBarLayout appBarLayout; 81 | @BindView(R.id.collapsing_toolbar_layout) 82 | CollapsingToolbarLayout collapsingToolbarLayout; 83 | @BindView(R.id.toolbar) 84 | Toolbar toolbar; 85 | @BindView(R.id.trailer_label_tv) 86 | TextView mTrailersLabel0; 87 | @BindView(R.id.trailers_hint_tv) 88 | TextView mTrailersLabel1; 89 | @BindView(R.id.reviews_label_tv) 90 | TextView mReviewsLabel0; 91 | @BindView(R.id.rating_value_tv) 92 | TextView mRatingTextView; 93 | @BindView(R.id.date_value_tv) 94 | TextView mDateTextView; 95 | @BindView(R.id.title_tv) 96 | TextView mTitleTextView; 97 | @BindView(R.id.plot_tv) 98 | TextView mPlotTextView; 99 | @BindView(R.id.poster_image_view) 100 | ImageView mPosterImageView; 101 | @BindView(R.id.rv_trailers) 102 | MovieRecyclerView mTrailerRecyclerView; 103 | @BindView(R.id.rv_reviews) 104 | MovieRecyclerView mReviewRecyclerView; 105 | @BindView(R.id.fav_button) 106 | FloatingActionButton mFavoriteButton; 107 | @BindView(R.id.backdrop_iv) 108 | ImageView mBackdropImageView; 109 | @BindView(R.id.director_value_tv) 110 | TextView mDirectorTextView; 111 | @BindView(R.id.cast_rv) 112 | RecyclerView mCastRecyclerView; 113 | @BindView(R.id.tagline_tv) 114 | TextView mTaglineTextView; 115 | @BindView(R.id.votes_value_tv) 116 | TextView mVotesTextView; 117 | @BindView(R.id.minutes_value_tv) 118 | TextView mMinutesTextView; 119 | @BindView(R.id.imdb_value_tv) 120 | ImageButton mImdbButton; 121 | @BindView(R.id.genres_rv) 122 | RecyclerView mGenresRecyclerView; 123 | @BindView(R.id.similar_movies_rv) 124 | RecyclerView mSimilarMoviesRecyclerView; 125 | private TrailerRecyclerViewAdapter mTrailerAdapter; 126 | private ReviewRecyclerViewAdapter mReviewAdapter; 127 | private GenresRecyclerViewAdapter mGenreAdapter; 128 | private MovieRecyclerViewAdapter mSimilarMoviesAdapter; 129 | private Context mContext; 130 | private ArrayList mTrailerTitles = new ArrayList<>(); 131 | private ArrayList mTrailerPaths = new ArrayList<>(); 132 | private ArrayList mReviewAuthors = new ArrayList<>(); 133 | private ArrayList mReviewContents = new ArrayList<>(); 134 | private ArrayList mGenres = new ArrayList<>(); 135 | private ArrayList mSimilarMovies = new ArrayList<>(); 136 | private byte[] imageBytes; 137 | private RealmDataSource dataSource; 138 | 139 | @Override 140 | protected void onCreate(Bundle savedInstanceState) { 141 | super.onCreate(savedInstanceState); 142 | setContentView(R.layout.activity_details); 143 | ButterKnife.bind(this); 144 | getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.colorPrimary)); 145 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 146 | getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.colorPrimaryDark)); 147 | } 148 | mContext = getApplicationContext(); 149 | if (Build.VERSION.SDK_INT >= 21) { 150 | Slide slide = new Slide(Gravity.BOTTOM); 151 | getWindow().setEnterTransition(slide); 152 | postponeEnterTransition(); 153 | } 154 | setSupportActionBar(toolbar); 155 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 156 | mMovie = getIntent().getParcelableExtra("movie"); 157 | collapsingToolbarLayout.setTitle(mMovie.getTitle()); 158 | collapsingToolbarLayout.setExpandedTitleColor(getResources().getColor(android.R.color.transparent)); 159 | collapsingToolbarLayout.setCollapsedTitleTextColor(getResources().getColor(android.R.color.white)); 160 | appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { 161 | if (Math.abs(verticalOffset) - appBarLayout.getTotalScrollRange() == 0) 162 | mPosterImageView.setVisibility(View.GONE); 163 | else 164 | mPosterImageView.setVisibility(View.VISIBLE); 165 | }); 166 | 167 | dataSource = new RealmDataSource(); 168 | dataSource.open(); 169 | 170 | mRatingTextView.setText(mMovie.getRating()); 171 | if (mMovie.getDate() != null && !mMovie.getDate().equals("")) 172 | mDateTextView.setText(prettifyDate(mMovie.getDate())); 173 | mTitleTextView.setText(mMovie.getTitle()); 174 | mPlotTextView.setText(mMovie.getPlot()); 175 | favButtonInit(mMovie.getId()); 176 | GlideApp.with(getApplicationContext()) 177 | .load(RetrofitAPI.BACKDROP_BASE_URL + mMovie.getBackdropPath()) 178 | .centerCrop() 179 | .placeholder(R.drawable.tmdb_placeholder_land) 180 | .error(R.drawable.tmdb_placeholder_land) 181 | .fallback(R.drawable.tmdb_placeholder_land) 182 | .transition(new DrawableTransitionOptions().crossFade()) 183 | .into(mBackdropImageView); 184 | if (mMovie.getPosterBytes() != null) { 185 | GlideApp.with(getApplicationContext()) 186 | .load(mMovie.getPosterBytes()) 187 | .centerCrop() 188 | .error(R.drawable.tmdb_placeholder) 189 | .fallback(R.drawable.tmdb_placeholder) 190 | .transition(new DrawableTransitionOptions().crossFade()) 191 | .into(mPosterImageView); 192 | } else { 193 | GlideApp.with(mContext) 194 | .load(RetrofitAPI.POSTER_BASE_URL + mMovie.getPosterPath()) 195 | .error(R.drawable.tmdb_placeholder) 196 | .fallback(R.drawable.tmdb_placeholder) 197 | .centerCrop() 198 | .transition(new DrawableTransitionOptions().crossFade()) 199 | .into(mPosterImageView); 200 | } 201 | 202 | if (!NetworkUtils.hasNetwork(mContext)) { 203 | (findViewById(R.id.tagline_tv)).setVisibility(View.GONE); 204 | (findViewById(R.id.similar_label_tv)).setVisibility(View.GONE); 205 | (findViewById(R.id.cast_label_tv)).setVisibility(View.GONE); 206 | (findViewById(R.id.votes_label_tv)).setVisibility(View.GONE); 207 | (findViewById(R.id.votes_value_tv)).setVisibility(View.GONE); 208 | (findViewById(R.id.minutes_label_tv)).setVisibility(View.GONE); 209 | (findViewById(R.id.minutes_value_tv)).setVisibility(View.GONE); 210 | (findViewById(R.id.imdb_label_tv)).setVisibility(View.GONE); 211 | (findViewById(R.id.imdb_value_tv)).setVisibility(View.GONE); 212 | (findViewById(R.id.director_label_tv)).setVisibility(View.GONE); 213 | (findViewById(R.id.director_value_tv)).setVisibility(View.GONE); 214 | (findViewById(R.id.genres_label_tv)).setVisibility(View.GONE); 215 | (findViewById(R.id.trailers_hint_tv)).setVisibility(View.GONE); 216 | (findViewById(R.id.trailer_label_tv)).setVisibility(View.GONE); 217 | (findViewById(R.id.reviews_label_tv)).setVisibility(View.GONE); 218 | } else { 219 | fetchCredits(); 220 | fetchMoreDetails(); 221 | 222 | mTrailerRecyclerView.setLayoutManager(new LinearLayoutManager(DetailsActivity.this, RecyclerView.HORIZONTAL, false)); 223 | mTrailerAdapter = new TrailerRecyclerViewAdapter(mContext, mTrailerTitles, mTrailerPaths, this); 224 | mTrailerRecyclerView.setAdapter(new ScaleInAnimationAdapter(mTrailerAdapter)); 225 | 226 | mReviewRecyclerView.setLayoutManager(new LinearLayoutManager(DetailsActivity.this, RecyclerView.VERTICAL, false)); 227 | mReviewAdapter = new ReviewRecyclerViewAdapter(mContext, mReviewAuthors, mReviewContents); 228 | mReviewRecyclerView.setAdapter(mReviewAdapter); 229 | 230 | mGenresRecyclerView.setLayoutManager(new LinearLayoutManager(DetailsActivity.this, RecyclerView.HORIZONTAL, false)); 231 | mGenreAdapter = new GenresRecyclerViewAdapter(mContext, mGenres); 232 | mGenresRecyclerView.setAdapter(new ScaleInAnimationAdapter(mGenreAdapter)); 233 | 234 | mSimilarMoviesRecyclerView.setLayoutManager(new LinearLayoutManager(DetailsActivity.this, RecyclerView.HORIZONTAL, false)); 235 | mSimilarMoviesAdapter = new MovieRecyclerViewAdapter(mContext, mSimilarMovies, (position, posterImageView) -> CookieBar.build(DetailsActivity.this) 236 | .setBackgroundColor(android.R.color.holo_green_dark) 237 | .setTitle(mSimilarMovies.get(position).getTitle()) 238 | .setMessage("Rating: " + mSimilarMovies.get(position).getRating() + " \nRelease: " + mSimilarMovies.get(position).getDate()) 239 | .show()); 240 | mSimilarMoviesRecyclerView.setAdapter(new ScaleInAnimationAdapter(mSimilarMoviesAdapter)); 241 | 242 | fetchDetails(mMovie.getId(), TRAILERS_DETAILS_TYPE); 243 | fetchDetails(mMovie.getId(), REVIEWS_DETAILS_TYPE); 244 | fetchSimilarMovies(mMovie.getId()); 245 | } 246 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 247 | startPostponedEnterTransition(); 248 | } 249 | } 250 | 251 | private void fetchSimilarMovies(int id) { 252 | RetrofitAPI retrofitAPI = NetworkUtils.getCacheEnabledRetrofit(getApplicationContext()).create(RetrofitAPI.class); 253 | Call similarMoviesCall = retrofitAPI.getSimilarMovies(id, BuildConfig.TMDB_API_TOKEN, "en-US"); 254 | similarMoviesCall.enqueue(new Callback() { 255 | @Override 256 | public void onResponse(Call call, Response response) { 257 | if (response.body() != null && response.body().getResults() != null && response.body().getResults().size() != 0) { 258 | mSimilarMovies.addAll(response.body().getResults()); 259 | mSimilarMoviesAdapter.notifyDataSetChanged(); 260 | } else { 261 | (findViewById(R.id.similar_label_tv)).setVisibility(View.GONE); 262 | mSimilarMoviesRecyclerView.setVisibility(View.GONE); 263 | } 264 | } 265 | 266 | @Override 267 | public void onFailure(Call call, Throwable t) { 268 | // Do I really have to do this? 269 | } 270 | }); 271 | } 272 | 273 | private void fetchDetails(int movieId, int detailsType) { 274 | RetrofitAPI retrofitAPI = NetworkUtils.getCacheEnabledRetrofit(getApplicationContext()).create(RetrofitAPI.class); 275 | switch (detailsType) { 276 | case TRAILERS_DETAILS_TYPE: 277 | mTrailerRecyclerView.setVisibility(View.GONE); 278 | mTrailersLabel0.setVisibility(View.GONE); 279 | mTrailersLabel1.setVisibility(View.GONE); 280 | Call trailerResponseCall = retrofitAPI.getTrailers(movieId, BuildConfig.TMDB_API_TOKEN, "en-US"); 281 | trailerResponseCall.enqueue(new Callback() { 282 | @Override 283 | public void onResponse(Call call, Response response) { 284 | TMDBTrailerResponse tmdbTrailerResponse = response.body(); 285 | if (tmdbTrailerResponse != null && tmdbTrailerResponse.getResults().size() != 0) { 286 | mTrailerTitles.clear(); 287 | mTrailerPaths.clear(); 288 | for (Trailer trailer : tmdbTrailerResponse.getResults()) { 289 | mTrailerTitles.add(trailer.getName()); 290 | mTrailerPaths.add(trailer.getKey()); 291 | } 292 | mTrailerAdapter.notifyDataSetChanged(); 293 | mTrailerRecyclerView.setVisibility(View.VISIBLE); 294 | mTrailersLabel0.setVisibility(View.VISIBLE); 295 | mTrailersLabel1.setVisibility(View.VISIBLE); 296 | } 297 | } 298 | 299 | @Override 300 | public void onFailure(Call call, Throwable t) { 301 | 302 | } 303 | }); 304 | break; 305 | case REVIEWS_DETAILS_TYPE: 306 | mReviewsLabel0.setVisibility(View.GONE); 307 | mReviewRecyclerView.setVisibility(View.GONE); 308 | Call reviewResponseCall = retrofitAPI.getReviews(movieId, BuildConfig.TMDB_API_TOKEN, "en-US"); 309 | reviewResponseCall.enqueue(new Callback() { 310 | @Override 311 | public void onResponse(Call call, Response response) { 312 | TMDBReviewResponse tmdbReviewResponse = response.body(); 313 | if (tmdbReviewResponse != null && tmdbReviewResponse.getResults().size() != 0) { 314 | mReviewAuthors.clear(); 315 | mReviewContents.clear(); 316 | for (Review review : tmdbReviewResponse.getResults()) { 317 | mReviewAuthors.add(review.getAuthor()); 318 | mReviewContents.add(review.getContent()); 319 | } 320 | mReviewAdapter.notifyDataSetChanged(); 321 | mReviewRecyclerView.setVisibility(View.VISIBLE); 322 | mReviewsLabel0.setVisibility(View.VISIBLE); 323 | } 324 | } 325 | 326 | @Override 327 | public void onFailure(Call call, Throwable t) { 328 | 329 | } 330 | }); 331 | break; 332 | } 333 | } 334 | 335 | private void fetchCredits() { 336 | LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false); 337 | mCastRecyclerView.setLayoutManager(layoutManager); 338 | 339 | final ArrayList castList = new ArrayList<>(); 340 | final CastRecyclerViewAdapter mCastAdapter = new CastRecyclerViewAdapter(this, castList, actorName -> { 341 | try { 342 | Uri uri = Uri.parse("https://www.google.com/search?q=" + actorName + " movies"); 343 | Intent actorMoviesIntent = new Intent(Intent.ACTION_VIEW, uri); 344 | startActivity(actorMoviesIntent); 345 | } catch (Exception e) { 346 | // Who doesn't have Google? Or a browser? 347 | e.printStackTrace(); 348 | } 349 | }); 350 | mCastRecyclerView.setAdapter(new ScaleInAnimationAdapter(mCastAdapter)); 351 | 352 | RetrofitAPI retrofitAPI = NetworkUtils.getCacheEnabledRetrofit(getApplicationContext()).create(RetrofitAPI.class); 353 | final Call creditsCall = retrofitAPI.getCredits(mMovie.getId(), BuildConfig.TMDB_API_TOKEN); 354 | creditsCall.enqueue(new Callback() { 355 | @Override 356 | public void onResponse(Call call, Response response) { 357 | TMDBCreditsResponse creditsResponse = response.body(); 358 | 359 | // Get cast info 360 | castList.clear(); 361 | if (creditsResponse != null && creditsResponse.getCast().size() != 0) { 362 | castList.addAll(creditsResponse.getCast()); 363 | mCastAdapter.notifyDataSetChanged(); 364 | } else { 365 | (findViewById(R.id.cast_label_tv)).setVisibility(View.GONE); 366 | mCastRecyclerView.setVisibility(View.GONE); 367 | } 368 | 369 | // Get director info 370 | if (creditsResponse != null) { 371 | for (Crew crew : creditsResponse.getCrew()) { 372 | if (crew.getJob().equals("Director")) { 373 | mDirectorTextView.setText(crew.getName()); 374 | break; 375 | } 376 | } 377 | } 378 | } 379 | 380 | @Override 381 | public void onFailure(Call call, Throwable t) { 382 | // Why bother doing anything here? 383 | } 384 | }); 385 | } 386 | 387 | private void fetchMoreDetails() { 388 | RetrofitAPI retrofitAPI = NetworkUtils.getCacheEnabledRetrofit(getApplicationContext()).create(RetrofitAPI.class); 389 | Call detailsResponseCall = retrofitAPI.getDetails(mMovie.getId(), BuildConfig.TMDB_API_TOKEN, "en-US"); 390 | detailsResponseCall.enqueue(new Callback() { 391 | @Override 392 | public void onResponse(Call call, Response response) { 393 | final TMDBDetailsResponse tmdbDetailsResponse = response.body(); 394 | String tagline = null; 395 | if (tmdbDetailsResponse != null) { 396 | tagline = tmdbDetailsResponse.getTagline(); 397 | } 398 | if (tagline != null && !tagline.equals("")) { 399 | mTaglineTextView.setText(tagline); 400 | } else { 401 | mTaglineTextView.setVisibility(View.GONE); 402 | } 403 | mVotesTextView.setText(String.valueOf(tmdbDetailsResponse.getVoteCount())); 404 | mMinutesTextView.setText(String.valueOf(tmdbDetailsResponse.getRuntime())); 405 | mImdbButton.setOnClickListener(view -> { 406 | String imdbId = tmdbDetailsResponse.getImdbId(); 407 | try { 408 | Uri uri; 409 | if (imdbId != null && !imdbId.equals("")) 410 | uri = Uri.parse("http://www.imdb.com/title/" + imdbId + "/"); 411 | else { 412 | Toast.makeText(mContext, "Movie isn't there on IMDB. Here is a Google search for it instead!", Toast.LENGTH_LONG).show(); 413 | uri = Uri.parse("https://www.google.com/search?q=" + tmdbDetailsResponse.getTitle()); 414 | } 415 | Intent imdbIntent = new Intent(Intent.ACTION_VIEW, uri); 416 | startActivity(imdbIntent); 417 | } catch (Exception e) { 418 | e.printStackTrace(); 419 | } 420 | }); 421 | if (tmdbDetailsResponse.getGenres() != null && tmdbDetailsResponse.getGenres().size() != 0) { 422 | mGenres.clear(); 423 | mGenres.addAll(tmdbDetailsResponse.getGenres()); 424 | mGenreAdapter.notifyDataSetChanged(); 425 | } else { 426 | (findViewById(R.id.genres_label_tv)).setVisibility(View.GONE); 427 | mGenresRecyclerView.setVisibility(View.GONE); 428 | } 429 | } 430 | 431 | @Override 432 | public void onFailure(Call call, Throwable t) { 433 | // Why bother doing anything here 434 | } 435 | }); 436 | } 437 | 438 | @Override 439 | public void onItemClick(String stringUrlTrailerClicked) { 440 | Uri youtubeUri = Uri.parse("https://www.youtube.com/watch?v=" + stringUrlTrailerClicked); 441 | Intent openYoutube = new Intent(Intent.ACTION_VIEW, youtubeUri); 442 | startActivity(openYoutube); 443 | } 444 | 445 | private String prettifyDate(String jsonDate) { 446 | DateFormat sourceDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 447 | Date date = null; 448 | try { 449 | date = sourceDateFormat.parse(jsonDate); 450 | } catch (ParseException e) { 451 | e.printStackTrace(); 452 | } 453 | DateFormat destDateFormat = new SimpleDateFormat("MMM dd\nyyyy"); 454 | String dateStr = destDateFormat.format(date); 455 | return dateStr; 456 | } 457 | 458 | private void favButtonInit(final int id) { 459 | Movie checkedMovie = dataSource.findMovieWithId(id); 460 | if (checkedMovie == null) 461 | mFavoriteButton.setImageResource(R.drawable.ic_favorite_border); 462 | else 463 | mFavoriteButton.setImageResource(R.drawable.ic_favorite); 464 | mFavoriteButton.setOnClickListener(view -> { 465 | Movie transactedMovie = dataSource.findMovieWithId(id); 466 | if (transactedMovie == null) { 467 | tempMovie = mMovie; 468 | GlideApp.with(mContext) 469 | .load(tempMovie.getPosterPath()) 470 | .centerCrop() 471 | .into(new Target() { 472 | @Override 473 | public void onLoadStarted(@Nullable Drawable placeholder) { 474 | 475 | } 476 | 477 | @Override 478 | public void onLoadFailed(@Nullable Drawable errorDrawable) { 479 | 480 | } 481 | 482 | @Override 483 | public void onResourceReady(Drawable resource, Transition transition) { 484 | Bitmap bitmap = ((BitmapDrawable) resource).getBitmap(); 485 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 486 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); 487 | imageBytes = stream.toByteArray(); 488 | } 489 | 490 | @Override 491 | public void onLoadCleared(@Nullable Drawable placeholder) { 492 | 493 | } 494 | 495 | @Override 496 | public void getSize(SizeReadyCallback cb) { 497 | 498 | } 499 | 500 | @Override 501 | public void removeCallback(SizeReadyCallback cb) { 502 | 503 | } 504 | 505 | @Nullable 506 | @Override 507 | public Request getRequest() { 508 | return null; 509 | } 510 | 511 | @Override 512 | public void setRequest(@Nullable Request request) { 513 | 514 | } 515 | 516 | @Override 517 | public void onStart() { 518 | 519 | } 520 | 521 | @Override 522 | public void onStop() { 523 | 524 | } 525 | 526 | @Override 527 | public void onDestroy() { 528 | 529 | } 530 | }); 531 | tempMovie.setPosterBytes(imageBytes); 532 | dataSource.addMovieToFavs(tempMovie); 533 | mFavoriteButton.setImageResource(R.drawable.ic_favorite); 534 | CookieBar.build(DetailsActivity.this) 535 | .setBackgroundColor(android.R.color.holo_blue_dark) 536 | .setTitle("Movie added to favorites!") 537 | .setMessage("You can now see the details, even when offline, in your Favorites.") 538 | .show(); 539 | } else { 540 | CookieBar.build(DetailsActivity.this) 541 | .setBackgroundColor(android.R.color.holo_red_dark) 542 | .setTitle("Movie removed from favorites!") 543 | .setMessage("But did you really have to? :-(") 544 | .show(); 545 | dataSource.deleteMovieFromFavs(transactedMovie); 546 | mFavoriteButton.setImageResource(R.drawable.ic_favorite_border); 547 | } 548 | }); 549 | } 550 | 551 | @Override 552 | public boolean onOptionsItemSelected(MenuItem item) { 553 | switch (item.getItemId()) { 554 | case android.R.id.home: 555 | NavUtils.navigateUpFromSameTask(this); 556 | return true; 557 | } 558 | return super.onOptionsItemSelected(item); 559 | } 560 | 561 | @Override 562 | protected void onDestroy() { 563 | super.onDestroy(); 564 | dataSource.close(); 565 | } 566 | 567 | } 568 | --------------------------------------------------------------------------------