package com.battlelancer.seriesguide.ui; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.ContextCompat; import android.support.v4.content.Loader; import android.support.v4.graphics.ColorUtils; import android.support.v4.util.SparseArrayCompat; import android.support.v4.widget.NestedScrollView; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; import com.battlelancer.seriesguide.R; import com.battlelancer.seriesguide.SgApp; import com.battlelancer.seriesguide.api.Action; import com.battlelancer.seriesguide.backend.settings.HexagonSettings; import com.battlelancer.seriesguide.extensions.ActionsHelper; import com.battlelancer.seriesguide.extensions.ExtensionManager; import com.battlelancer.seriesguide.extensions.MovieActionsContract; import com.battlelancer.seriesguide.items.MovieDetails; import com.battlelancer.seriesguide.loaders.MovieCreditsLoader; import com.battlelancer.seriesguide.loaders.MovieLoader; import com.battlelancer.seriesguide.loaders.MovieTrailersLoader; import com.battlelancer.seriesguide.settings.DisplaySettings; import com.battlelancer.seriesguide.settings.TmdbSettings; import com.battlelancer.seriesguide.settings.TraktCredentials; import com.battlelancer.seriesguide.ui.dialogs.LanguageChoiceDialogFragment; import com.battlelancer.seriesguide.ui.dialogs.MovieCheckInDialogFragment; import com.battlelancer.seriesguide.ui.dialogs.RateDialogFragment; import com.battlelancer.seriesguide.util.LanguageTools; import com.battlelancer.seriesguide.util.MovieTools; import com.battlelancer.seriesguide.util.PeopleListHelper; import com.battlelancer.seriesguide.util.ServiceUtils; import com.battlelancer.seriesguide.util.ShareUtils; import com.battlelancer.seriesguide.util.TimeTools; import com.battlelancer.seriesguide.util.TmdbTools; import com.battlelancer.seriesguide.util.TraktTools; import com.battlelancer.seriesguide.util.Utils; import com.readystatesoftware.systembartint.SystemBarTintManager; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; import com.uwetrottmann.androidutils.AndroidUtils; import com.uwetrottmann.androidutils.CheatSheet; import com.uwetrottmann.tmdb2.entities.Credits; import com.uwetrottmann.tmdb2.entities.Movie; import com.uwetrottmann.tmdb2.entities.Videos; import com.uwetrottmann.trakt5.entities.Ratings; import java.util.ArrayList; import java.util.List; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import timber.log.Timber; /** * Displays details about one movie including plot, ratings, trailers and a poster. */ public class MovieDetailsFragment extends Fragment implements MovieActionsContract { public static MovieDetailsFragment newInstance(int tmdbId) { MovieDetailsFragment f = new MovieDetailsFragment(); Bundle args = new Bundle(); args.putInt(InitBundle.TMDB_ID, tmdbId); f.setArguments(args); return f; } public interface InitBundle { String TMDB_ID = "tmdbid"; } private static final String TAG = "Movie Details"; private Unbinder unbinder; @BindView(R.id.rootLayoutMovie) FrameLayout rootLayoutMovie; @BindView(R.id.progressBar) View progressBar; @BindView(R.id.containerMovieButtons) View containerMovieButtons; @BindView(R.id.dividerMovieButtons) View dividerMovieButtons; @BindView(R.id.buttonMovieCheckIn) Button buttonMovieCheckIn; @BindView(R.id.buttonMovieWatched) Button buttonMovieWatched; @BindView(R.id.buttonMovieCollected) Button buttonMovieCollected; @BindView(R.id.buttonMovieWatchlisted) Button buttonMovieWatchlisted; @BindView(R.id.containerRatings) View containerRatings; @BindView(R.id.textViewRatingsTmdbValue) TextView textViewRatingsTmdbValue; @BindView(R.id.textViewRatingsTmdbVotes) TextView textViewRatingsTmdbVotes; @BindView(R.id.textViewRatingsTraktVotes) TextView textViewRatingsTraktVotes; @BindView(R.id.textViewRatingsTraktValue) TextView textViewRatingsTraktValue; @BindView(R.id.textViewRatingsTraktUserLabel) TextView textViewRatingsTraktUserLabel; @BindView(R.id.textViewRatingsTraktUser) TextView textViewRatingsTraktUser; @BindView(R.id.contentContainerMovie) NestedScrollView contentContainerMovie; @Nullable @BindView(R.id.contentContainerMovieRight) NestedScrollView contentContainerMovieRight; @BindView(R.id.frameLayoutMoviePoster) FrameLayout frameLayoutMoviePoster; @BindView(R.id.imageViewMoviePoster) ImageView imageViewMoviePoster; @BindView(R.id.textViewMovieTitle) TextView textViewMovieTitle; @BindView(R.id.textViewMovieDescription) TextView textViewMovieDescription; @BindView(R.id.textViewMovieDate) TextView textViewMovieDate; @BindView(R.id.textViewMovieGenresLabel) View textViewMovieGenresLabel; @BindView(R.id.textViewMovieGenres) TextView textViewMovieGenres; @BindView(R.id.containerCast) ViewGroup containerCast; @BindView(R.id.labelCast) View labelCast; @BindView(R.id.containerCrew) ViewGroup containerCrew; @BindView(R.id.labelCrew) View labelCrew; @BindView(R.id.buttonMovieLanguage) Button buttonMovieLanguage; @BindView(R.id.buttonMovieComments) Button buttonMovieComments; @BindView(R.id.containerMovieActions) ViewGroup containerMovieActions; private int tmdbId; private MovieDetails movieDetails = new MovieDetails(); private Videos.Video trailer; private int currentLanguageIndex; private Handler handler = new Handler(); private AsyncTask<Bitmap, Void, Palette> paletteAsyncTask; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_movie, container, false); unbinder = ButterKnife.bind(this, view); progressBar.setVisibility(View.VISIBLE); textViewMovieGenresLabel.setVisibility(View.GONE); // important action buttons containerMovieButtons.setVisibility(View.GONE); containerRatings.setVisibility(View.GONE); // language button buttonMovieLanguage.setVisibility(View.GONE); Utils.setVectorCompoundDrawable(getActivity().getTheme(), buttonMovieLanguage, R.attr.drawableLanguage); CheatSheet.setup(buttonMovieLanguage, R.string.pref_language); buttonMovieLanguage.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // guard against onClick called after fragment is up navigated (multi-touch) // onSaveInstanceState might already be called if (isResumed()) { DialogFragment dialog = LanguageChoiceDialogFragment.newInstance( currentLanguageIndex); dialog.show(getFragmentManager(), "dialog-language"); } } }); // comments button buttonMovieComments.setVisibility(View.GONE); Utils.setVectorCompoundDrawable(getActivity().getTheme(), buttonMovieComments, R.attr.drawableComments); // cast and crew setCastVisibility(false); setCrewVisibility(false); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); tmdbId = getArguments().getInt(InitBundle.TMDB_ID); if (tmdbId <= 0) { getFragmentManager().popBackStack(); return; } setupViews(); Bundle args = new Bundle(); args.putInt(InitBundle.TMDB_ID, tmdbId); getLoaderManager().initLoader(MovieDetailsActivity.LOADER_ID_MOVIE, args, mMovieLoaderCallbacks); getLoaderManager().initLoader(MovieDetailsActivity.LOADER_ID_MOVIE_TRAILERS, args, mMovieTrailerLoaderCallbacks); getLoaderManager().initLoader(MovieDetailsActivity.LOADER_ID_MOVIE_CREDITS, args, mMovieCreditsLoaderCallbacks); setHasOptionsMenu(true); } private void setupViews() { final int decorationHeightPx; if (AndroidUtils.isKitKatOrHigher()) { // avoid overlap with status + action bar (adjust top margin) // warning: status bar not always translucent (e.g. Nexus 10) // (using fitsSystemWindows would not work correctly with multiple views) SystemBarTintManager.SystemBarConfig config = ((MovieDetailsActivity) getActivity()).getSystemBarTintManager().getConfig(); int pixelInsetTop = config.getPixelInsetTop(false); // action bar height is pre-set as top margin, add to it decorationHeightPx = pixelInsetTop + contentContainerMovie.getPaddingTop(); contentContainerMovie.setPadding(0, decorationHeightPx, 0, 0); // dual pane layout? if (contentContainerMovieRight != null) { contentContainerMovieRight.setPadding(0, decorationHeightPx, 0, 0); } } else { // content container has actionBarSize top padding by default decorationHeightPx = contentContainerMovie.getPaddingTop(); } // show toolbar title and background when scrolling final int defaultPaddingPx = getResources().getDimensionPixelSize(R.dimen.default_padding); NestedScrollView.OnScrollChangeListener scrollChangeListener = new ToolbarScrollChangeListener(defaultPaddingPx, decorationHeightPx); contentContainerMovie.setOnScrollChangeListener(scrollChangeListener); if (contentContainerMovieRight != null) { contentContainerMovieRight.setOnScrollChangeListener(scrollChangeListener); } } @Override public void onStart() { super.onStart(); BaseNavDrawerActivity.ServiceActiveEvent event = EventBus.getDefault() .getStickyEvent(BaseNavDrawerActivity.ServiceActiveEvent.class); setMovieButtonsEnabled(event == null); EventBus.getDefault().register(this); } @Override public void onResume() { super.onResume(); // refresh actions when returning, enabled extensions or their actions might have changed loadMovieActionsDelayed(); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } @Override public void onDestroyView() { super.onDestroyView(); // Always cancel the request here, this is safe to call even if the image has been loaded. // This ensures that the anonymous callback we have does not prevent the fragment from // being garbage collected. It also prevents our callback from getting invoked even after the // fragment is destroyed. Picasso.with(getContext()).cancelRequest(imageViewMoviePoster); // same for Palette task if (paletteAsyncTask != null) { paletteAsyncTask.cancel(true); } unbinder.unbind(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); if (movieDetails != null) { // choose theme variant boolean isLightTheme = SeriesGuidePreferences.THEME == R.style.Theme_SeriesGuide_Light; inflater.inflate( isLightTheme ? R.menu.movie_details_menu_light : R.menu.movie_details_menu, menu); // enable/disable actions boolean isEnableShare = movieDetails.tmdbMovie() != null && !TextUtils.isEmpty( movieDetails.tmdbMovie().title); MenuItem shareItem = menu.findItem(R.id.menu_movie_share); shareItem.setEnabled(isEnableShare); shareItem.setVisible(isEnableShare); boolean isEnableImdb = movieDetails.tmdbMovie() != null && !TextUtils.isEmpty(movieDetails.tmdbMovie().imdb_id); MenuItem imdbItem = menu.findItem(R.id.menu_open_imdb); imdbItem.setEnabled(isEnableImdb); imdbItem.setVisible(isEnableImdb); boolean isEnableYoutube = trailer != null; MenuItem youtubeItem = menu.findItem(R.id.menu_open_youtube); youtubeItem.setEnabled(isEnableYoutube); youtubeItem.setVisible(isEnableYoutube); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_movie_share) { ShareUtils.shareMovie(getActivity(), tmdbId, movieDetails.tmdbMovie().title); Utils.trackAction(getActivity(), TAG, "Share"); return true; } if (itemId == R.id.menu_open_imdb) { ServiceUtils.openImdb(movieDetails.tmdbMovie().imdb_id, TAG, getActivity()); return true; } if (itemId == R.id.menu_open_youtube) { ServiceUtils.openYoutube(trailer.key, TAG, getActivity()); return true; } if (itemId == R.id.menu_open_tmdb) { TmdbTools.openTmdbMovie(getActivity(), tmdbId, TAG); } if (itemId == R.id.menu_open_trakt) { Utils.launchWebsite(getActivity(), TraktTools.buildMovieUrl(tmdbId), TAG, "trakt"); return true; } return super.onOptionsItemSelected(item); } private void populateMovieViews() { /* Get everything from TMDb. Also get additional rating from trakt. */ final Ratings traktRatings = movieDetails.traktRatings(); final Movie tmdbMovie = movieDetails.tmdbMovie(); final boolean inCollection = movieDetails.inCollection; final boolean inWatchlist = movieDetails.inWatchlist; final boolean isWatched = movieDetails.isWatched; final int rating = movieDetails.userRating; textViewMovieTitle.setText(tmdbMovie.title); getActivity().setTitle(tmdbMovie.title); textViewMovieDescription.setText(tmdbMovie.overview); // release date and runtime: "July 17, 2009 | 95 min" StringBuilder releaseAndRuntime = new StringBuilder(); if (tmdbMovie.release_date != null) { releaseAndRuntime.append( TimeTools.formatToLocalDate(getContext(), tmdbMovie.release_date)); releaseAndRuntime.append(" | "); } releaseAndRuntime.append( getString(R.string.runtime_minutes, String.valueOf(tmdbMovie.runtime))); textViewMovieDate.setText(releaseAndRuntime.toString()); // check-in button final String title = tmdbMovie.title; buttonMovieCheckIn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // display a check-in dialog MovieCheckInDialogFragment f = MovieCheckInDialogFragment .newInstance(tmdbId, title); f.show(getFragmentManager(), "checkin-dialog"); Utils.trackAction(getActivity(), TAG, "Check-In"); } }); CheatSheet.setup(buttonMovieCheckIn); // hide check-in if not connected to trakt or hexagon is enabled boolean isConnectedToTrakt = TraktCredentials.get(getActivity()).hasCredentials(); boolean displayCheckIn = isConnectedToTrakt && !HexagonSettings.isEnabled(getActivity()); buttonMovieCheckIn.setVisibility(displayCheckIn ? View.VISIBLE : View.GONE); dividerMovieButtons.setVisibility( displayCheckIn ? View.VISIBLE : View.GONE); // watched button (only supported when connected to trakt) if (isConnectedToTrakt) { buttonMovieWatched.setText( isWatched ? R.string.action_unwatched : R.string.action_watched); CheatSheet.setup(buttonMovieWatched, isWatched ? R.string.action_unwatched : R.string.action_watched); Utils.setCompoundDrawablesRelativeWithIntrinsicBounds(buttonMovieWatched, 0, isWatched ? Utils.resolveAttributeToResourceId(getActivity().getTheme(), R.attr.drawableWatched) : Utils.resolveAttributeToResourceId(getActivity().getTheme(), R.attr.drawableWatch), 0, 0); buttonMovieWatched.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (isWatched) { MovieTools.unwatchedMovie(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Unwatched movie"); } else { MovieTools.watchedMovie(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Watched movie"); } } }); buttonMovieWatched.setVisibility(View.VISIBLE); } else { buttonMovieWatched.setVisibility(View.GONE); } // collected button Utils.setCompoundDrawablesRelativeWithIntrinsicBounds(buttonMovieCollected, 0, inCollection ? R.drawable.ic_collected : Utils.resolveAttributeToResourceId(getActivity().getTheme(), R.attr.drawableCollect), 0, 0); buttonMovieCollected.setText(inCollection ? R.string.action_collection_remove : R.string.action_collection_add); CheatSheet.setup(buttonMovieCollected, inCollection ? R.string.action_collection_remove : R.string.action_collection_add); buttonMovieCollected.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (inCollection) { MovieTools.removeFromCollection(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Uncollected movie"); } else { MovieTools.addToCollection(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Collected movie"); } } }); // watchlist button Utils.setCompoundDrawablesRelativeWithIntrinsicBounds(buttonMovieWatchlisted, 0, inWatchlist ? R.drawable.ic_listed : Utils.resolveAttributeToResourceId(getActivity().getTheme(), R.attr.drawableList), 0, 0); buttonMovieWatchlisted.setText( inWatchlist ? R.string.watchlist_remove : R.string.watchlist_add); CheatSheet.setup(buttonMovieWatchlisted, inWatchlist ? R.string.watchlist_remove : R.string.watchlist_add); buttonMovieWatchlisted.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (inWatchlist) { MovieTools.removeFromWatchlist(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Unwatchlist movie"); } else { MovieTools.addToWatchlist(SgApp.from(getActivity()), tmdbId); Utils.trackAction(getActivity(), TAG, "Watchlist movie"); } } }); // show button bar containerMovieButtons.setVisibility(View.VISIBLE); // language button LanguageTools.LanguageData languageData = LanguageTools.getMovieLanguageData(getContext()); if (languageData != null) { currentLanguageIndex = languageData.languageIndex; buttonMovieLanguage.setText(languageData.languageString); } else { buttonMovieLanguage.setText(null); } buttonMovieLanguage.setVisibility(View.VISIBLE); // ratings textViewRatingsTmdbValue.setText( TraktTools.buildRatingString(tmdbMovie.vote_average)); textViewRatingsTmdbVotes.setText( TraktTools.buildRatingVotesString(getActivity(), tmdbMovie.vote_count)); if (traktRatings != null) { textViewRatingsTraktVotes.setText( TraktTools.buildRatingVotesString(getActivity(), traktRatings.votes)); textViewRatingsTraktValue.setText( TraktTools.buildRatingString(traktRatings.rating)); } // if movie is not in database, can't handle user ratings if (!inCollection && !inWatchlist && !isWatched) { textViewRatingsTraktUserLabel.setVisibility(View.GONE); textViewRatingsTraktUser.setVisibility(View.GONE); containerRatings.setClickable(false); containerRatings.setLongClickable(false); // cheat sheet } else { textViewRatingsTraktUserLabel.setVisibility(View.VISIBLE); textViewRatingsTraktUser.setVisibility(View.VISIBLE); textViewRatingsTraktUser.setText( TraktTools.buildUserRatingString(getActivity(), rating)); containerRatings.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { rateMovie(); } }); CheatSheet.setup(containerRatings, R.string.action_rate); } containerRatings.setVisibility(View.VISIBLE); // genres textViewMovieGenresLabel.setVisibility(View.VISIBLE); Utils.setValueOrPlaceholder(textViewMovieGenres, TmdbTools.buildGenresString(tmdbMovie.genres)); // trakt comments link buttonMovieComments.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent i = new Intent(getActivity(), TraktCommentsActivity.class); i.putExtras(TraktCommentsActivity.createInitBundleMovie(title, tmdbId)); Utils.startActivityWithAnimation(getActivity(), i, v); Utils.trackAction(v.getContext(), TAG, "Comments"); } }); buttonMovieComments.setVisibility(View.VISIBLE); // load poster, cache on external storage if (TextUtils.isEmpty(tmdbMovie.poster_path)) { frameLayoutMoviePoster.setClickable(false); frameLayoutMoviePoster.setFocusable(false); } else { final String smallImageUrl = TmdbSettings.getImageBaseUrl(getActivity()) + TmdbSettings.POSTER_SIZE_SPEC_W342 + tmdbMovie.poster_path; ServiceUtils.loadWithPicasso(getActivity(), smallImageUrl) .into(imageViewMoviePoster, new Callback.EmptyCallback() { @Override public void onSuccess() { Bitmap bitmap = ((BitmapDrawable) imageViewMoviePoster .getDrawable()).getBitmap(); paletteAsyncTask = Palette.from(bitmap) .generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { int color = palette.getVibrantColor(Color.WHITE); color = ColorUtils.setAlphaComponent(color, 50); rootLayoutMovie.setBackgroundColor(color); } }); } }); // click listener for high resolution poster frameLayoutMoviePoster.setFocusable(true); frameLayoutMoviePoster.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { String largeImageUrl = TmdbSettings.getImageBaseUrl(getActivity()) + TmdbSettings.POSTER_SIZE_SPEC_ORIGINAL + tmdbMovie.poster_path; Intent intent = new Intent(getActivity(), FullscreenImageActivity.class); intent.putExtra(FullscreenImageActivity.EXTRA_PREVIEW_IMAGE, smallImageUrl); intent.putExtra(FullscreenImageActivity.EXTRA_IMAGE, largeImageUrl); Utils.startActivityWithAnimation(getActivity(), intent, view); } }); } } private void populateMovieCreditsViews(final Credits credits) { if (credits == null) { setCastVisibility(false); setCrewVisibility(false); return; } // cast members if (credits.cast == null || credits.cast.size() == 0) { setCastVisibility(false); } else { setCastVisibility(true); PeopleListHelper.populateMovieCast(getActivity(), containerCast, credits, TAG); } // crew members if (credits.crew == null || credits.crew.size() == 0) { setCrewVisibility(false); } else { setCrewVisibility(true); PeopleListHelper.populateMovieCrew(getActivity(), containerCrew, credits, TAG); } } @Override @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(ExtensionManager.MovieActionReceivedEvent event) { if (event.movieTmdbId != tmdbId) { return; } loadMovieActionsDelayed(); } @Subscribe public void onEvent(MovieTools.MovieChangedEvent event) { if (event.movieTmdbId != tmdbId) { return; } // re-query to update movie details restartMovieLoader(); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEventEpisodeTask(BaseNavDrawerActivity.ServiceActiveEvent event) { setMovieButtonsEnabled(false); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEventEpisodeTask(BaseNavDrawerActivity.ServiceCompletedEvent event) { setMovieButtonsEnabled(true); } private void setMovieButtonsEnabled(boolean enabled) { buttonMovieCheckIn.setEnabled(enabled); buttonMovieWatched.setEnabled(enabled); buttonMovieCollected.setEnabled(enabled); buttonMovieWatchlisted.setEnabled(enabled); } @Subscribe(threadMode = ThreadMode.MAIN) public void handleLanguageEvent(LanguageChoiceDialogFragment.LanguageChangedEvent event) { if (!AndroidUtils.isNetworkConnected(getContext())) { Toast.makeText(getContext(), R.string.offline, Toast.LENGTH_LONG).show(); return; } String languageCode = getResources().getStringArray( R.array.languageCodesMovies)[event.selectedLanguageIndex]; PreferenceManager.getDefaultSharedPreferences(getContext()).edit() .putString(DisplaySettings.KEY_MOVIES_LANGUAGE, languageCode) .apply(); progressBar.setVisibility(View.VISIBLE); // reload movie details and trailers (but not cast/crew info which is not language dependent) restartMovieLoader(); Bundle args = new Bundle(); args.putInt(InitBundle.TMDB_ID, tmdbId); getLoaderManager().restartLoader(MovieDetailsActivity.LOADER_ID_MOVIE_TRAILERS, args, mMovieTrailerLoaderCallbacks); } @Override public void loadMovieActions() { List<Action> actions = ExtensionManager.getInstance(getContext()) .getLatestMovieActions(tmdbId); // no actions available yet, request extensions to publish them if (actions == null || actions.size() == 0) { actions = new ArrayList<>(); if (movieDetails.tmdbMovie() != null) { com.battlelancer.seriesguide.api.Movie movie = new com.battlelancer.seriesguide.api.Movie.Builder() .tmdbId(tmdbId) .imdbId(movieDetails.tmdbMovie().imdb_id) .title(movieDetails.tmdbMovie().title) .releaseDate(movieDetails.tmdbMovie().release_date) .build(); ExtensionManager.getInstance(getContext()).requestMovieActions(movie); } } Timber.d("loadMovieActions: received %s actions for %s", actions.size(), tmdbId); ActionsHelper.populateActions(getActivity().getLayoutInflater(), getActivity().getTheme(), containerMovieActions, actions, TAG); } Runnable movieActionsRunnable = new Runnable() { @Override public void run() { if (!isAdded()) { return; // we need an activity for this, abort. } loadMovieActions(); } }; @Override public void loadMovieActionsDelayed() { handler.removeCallbacks(movieActionsRunnable); handler.postDelayed(movieActionsRunnable, MovieActionsContract.ACTION_LOADER_DELAY_MILLIS); } private void rateMovie() { if (TraktCredentials.ensureCredentials(getActivity())) { RateDialogFragment newFragment = RateDialogFragment.newInstanceMovie(tmdbId); newFragment.show(getFragmentManager(), "ratedialog"); Utils.trackAction(getActivity(), TAG, "Rate (trakt)"); } } private void setCrewVisibility(boolean visible) { labelCrew.setVisibility(visible ? View.VISIBLE : View.GONE); containerCrew.setVisibility(visible ? View.VISIBLE : View.GONE); } private void setCastVisibility(boolean visible) { labelCast.setVisibility(visible ? View.VISIBLE : View.GONE); containerCast.setVisibility(visible ? View.VISIBLE : View.GONE); } private void restartMovieLoader() { Bundle args = new Bundle(); args.putInt(InitBundle.TMDB_ID, tmdbId); getLoaderManager().restartLoader(MovieDetailsActivity.LOADER_ID_MOVIE, args, mMovieLoaderCallbacks); } private LoaderManager.LoaderCallbacks<MovieDetails> mMovieLoaderCallbacks = new LoaderManager.LoaderCallbacks<MovieDetails>() { @Override public Loader<MovieDetails> onCreateLoader(int loaderId, Bundle args) { return new MovieLoader(SgApp.from(getActivity()), args.getInt(InitBundle.TMDB_ID)); } @Override public void onLoadFinished(Loader<MovieDetails> movieLoader, MovieDetails movieDetails) { if (!isAdded()) { return; } MovieDetailsFragment.this.movieDetails = movieDetails; progressBar.setVisibility(View.GONE); // we need at least values from database or tmdb if (movieDetails.tmdbMovie() != null) { populateMovieViews(); loadMovieActions(); getActivity().invalidateOptionsMenu(); } else { // if there is no local data and loading from network failed String emptyText; if (AndroidUtils.isNetworkConnected(getContext())) { emptyText = getString(R.string.api_error_generic, getString(R.string.tmdb)); } else { emptyText = getString(R.string.offline); } textViewMovieDescription.setText(emptyText); } } @Override public void onLoaderReset(Loader<MovieDetails> movieLoader) { // nothing to do } }; private LoaderManager.LoaderCallbacks<Videos.Video> mMovieTrailerLoaderCallbacks = new LoaderManager.LoaderCallbacks<Videos.Video>() { @Override public Loader<Videos.Video> onCreateLoader(int loaderId, Bundle args) { return new MovieTrailersLoader(SgApp.from(getActivity()), args.getInt(InitBundle.TMDB_ID)); } @Override public void onLoadFinished(Loader<Videos.Video> trailersLoader, Videos.Video trailer) { if (!isAdded()) { return; } if (trailer != null) { MovieDetailsFragment.this.trailer = trailer; getActivity().invalidateOptionsMenu(); } } @Override public void onLoaderReset(Loader<Videos.Video> trailersLoader) { // do nothing } }; private LoaderManager.LoaderCallbacks<Credits> mMovieCreditsLoaderCallbacks = new LoaderManager.LoaderCallbacks<Credits>() { @Override public Loader<Credits> onCreateLoader(int loaderId, Bundle args) { return new MovieCreditsLoader(SgApp.from(getActivity()), args.getInt(InitBundle.TMDB_ID)); } @Override public void onLoadFinished(Loader<Credits> creditsLoader, Credits credits) { if (!isAdded()) { return; } populateMovieCreditsViews(credits); } @Override public void onLoaderReset(Loader<Credits> creditsLoader) { // do nothing } }; private class ToolbarScrollChangeListener implements NestedScrollView.OnScrollChangeListener { private final int overlayThresholdPx; private final int titleThresholdPx; private SparseArrayCompat<Boolean> showOverlayMap; private boolean showOverlay; private boolean showTitle; public ToolbarScrollChangeListener(int overlayThresholdPx, int titleThresholdPx) { this.overlayThresholdPx = overlayThresholdPx; this.titleThresholdPx = titleThresholdPx; // we have determined by science that a capacity of 2 is good in our case :) showOverlayMap = new SparseArrayCompat<>(2); } @Override public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); if (actionBar == null) { return; } int viewId = v.getId(); boolean shouldShowOverlay = scrollY > overlayThresholdPx; showOverlayMap.put(viewId, shouldShowOverlay); for (int i = 0; i < showOverlayMap.size(); i++) { shouldShowOverlay |= showOverlayMap.valueAt(i); } if (!showOverlay && shouldShowOverlay) { int primaryColor = ContextCompat.getColor(v.getContext(), Utils.resolveAttributeToResourceId(v.getContext().getTheme(), R.attr.sgColorBackgroundDim)); actionBar.setBackgroundDrawable(new ColorDrawable(primaryColor)); } else if (showOverlay && !shouldShowOverlay) { actionBar.setBackgroundDrawable(null); } showOverlay = shouldShowOverlay; // only main container should show/hide title if (viewId == R.id.contentContainerMovie) { boolean shouldShowTitle = scrollY > titleThresholdPx; if (!showTitle && shouldShowTitle) { if (movieDetails != null && movieDetails.tmdbMovie() != null) { actionBar.setTitle(movieDetails.tmdbMovie().title); actionBar.setDisplayShowTitleEnabled(true); } } else if (showTitle && !shouldShowTitle) { actionBar.setDisplayShowTitleEnabled(false); } showTitle = shouldShowTitle; } } } }