com.burhan.udacity.popularmovies.ui.fragment.MovieFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.burhan.udacity.popularmovies.ui.fragment.MovieFragment.java

Source

/*
 * Copyright 2015.  Emin Yahyayev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.burhan.udacity.popularmovies.ui.fragment;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.Toolbar;
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.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.burhan.udacity.popularmovies.R;
import com.burhan.udacity.popularmovies.data.model.Movie;
import com.burhan.udacity.popularmovies.data.model.Review;
import com.burhan.udacity.popularmovies.data.model.Video;
import com.burhan.udacity.popularmovies.data.repository.MoviesRepository;
import com.burhan.udacity.popularmovies.ui.activity.MovieDetailsActivity;
import com.burhan.udacity.popularmovies.ui.helper.MoviesHelper;
import com.burhan.udacity.popularmovies.ui.module.MoviesModule;
import com.burhan.udacity.popularmovies.utils.Lists;
import com.burhan.udacity.popularmovies.utils.UiUtils;
import com.github.ksoichiro.android.observablescrollview.ObservableScrollView;
import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks;
import com.github.ksoichiro.android.observablescrollview.ScrollState;
import com.github.ksoichiro.android.observablescrollview.ScrollUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

import butterknife.Bind;
import butterknife.BindColor;
import butterknife.OnClick;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;

import static butterknife.ButterKnife.findById;

public final class MovieFragment extends BaseFragment implements ObservableScrollViewCallbacks {
    public static final String ARG_MOVIE = "arg_movie";

    private static final String STATE_SCROLL_VIEW = "state_scroll_view";
    private static final String STATE_REVIEWS = "state_reviews";
    private static final String STATE_VIDEOS = "state_trailers";

    @Nullable
    Toolbar mToolbar;

    @Bind(R.id.movie_scroll_view)
    ObservableScrollView mScrollView;
    @Bind(R.id.movie_poster)
    ImageView mPosterImage;
    @Bind(R.id.movie_poster_play)
    ImageView mPosterPlayImage;
    @Bind(R.id.movie_cover)
    ImageView mCoverImage;
    @Bind(R.id.movie_cover_container)
    FrameLayout mCoverContainer;

    @Bind(R.id.movie_favorite_button)
    ImageButton mFavoriteButton;
    @Bind(R.id.movie_title)
    TextView mTitle;
    @Bind(R.id.movie_release_date)
    TextView mReleaseDate;
    @Bind(R.id.movie_average_rating)
    TextView mRating;
    @Bind(R.id.movie_overview)
    TextView mOverview;
    @Bind(R.id.movie_reviews_container)
    ViewGroup mReviewsGroup;
    @Bind(R.id.movie_videos_container)
    ViewGroup mVideosGroup;

    @BindColor(R.color.theme_primary)
    int mColorThemePrimary;
    @BindColor(R.color.body_text_white)
    int mColorTextWhite;

    @Inject
    MoviesRepository mMoviesRepository;

    private MoviesHelper mHelper;
    private CompositeSubscription mSubscriptions;
    private List<Runnable> mDeferredUiOperations = new ArrayList<>();

    private Movie mMovie;
    private List<Review> mReviews;
    private List<Video> mVideos;
    private Video mTrailer;
    private MenuItem mMenuItemShare;

    public static MovieFragment newInstance(Movie movie) {
        Bundle args = new Bundle();
        args.putParcelable(ARG_MOVIE, movie);

        MovieFragment fragment = new MovieFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        setHasOptionsMenu(true);
        mHelper = new MoviesHelper(activity, mMoviesRepository);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_movie, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        trySetupToolbar();
        mScrollView.setScrollViewCallbacks(this);

        if (savedInstanceState != null) {
            mVideos = savedInstanceState.getParcelableArrayList(STATE_VIDEOS);
            mReviews = savedInstanceState.getParcelableArrayList(STATE_REVIEWS);
            mScrollView.onRestoreInstanceState(savedInstanceState.getParcelable(STATE_SCROLL_VIEW));
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mSubscriptions = new CompositeSubscription();

        onScrollChanged(mScrollView.getCurrentScrollY(), false, false);

        onMovieLoaded(getArguments().getParcelable(ARG_MOVIE));

        if (mReviews != null)
            onReviewsLoaded(mReviews);
        else
            loadReviews();

        if (mVideos != null)
            onVideosLoaded(mVideos);
        else
            loadVideos();

        // subscribe to global favored changes in order to synchronise movies from different views
        mSubscriptions.add(mHelper.getFavoredObservable()
                .filter(event -> ((mMovie != null) && (mMovie.getId() == event.movieId))).subscribe(movie -> {
                    mMovie.setFavored(movie.favored);
                    mFavoriteButton.setSelected(movie.favored);
                }));
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.fragment_movie, menu);
        mMenuItemShare = menu.findItem(R.id.menu_share);
        tryExecuteDeferredUiOperations();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_share) {
            if (mTrailer != null)
                mHelper.shareTrailer(R.string.share_template, mTrailer);
        }
        return true;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(STATE_SCROLL_VIEW, mScrollView.onSaveInstanceState());
        if (mReviews != null)
            outState.putParcelableArrayList(STATE_REVIEWS, new ArrayList<>(mReviews));
        if (mVideos != null)
            outState.putParcelableArrayList(STATE_VIDEOS, new ArrayList<>(mVideos));
    }

    @Override
    public void onDestroyView() {
        mSubscriptions.unsubscribe();
        super.onDestroyView();
    }

    @OnClick(R.id.movie_favorite_button)
    public void onFavored(ImageButton button) {
        if (mMovie == null)
            return;

        boolean favored = !mMovie.isFavored();
        button.setSelected(favored);
        mHelper.setMovieFavored(mMovie, favored);
        if (favored)
            showToast(R.string.message_movie_favored);
    }

    @Override
    public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
        ViewCompat.setTranslationY(mCoverContainer, scrollY / 2);

        if (mToolbar != null) {
            int parallaxImageHeight = mCoverContainer.getMeasuredHeight();
            float alpha = Math.min(1, (float) scrollY / parallaxImageHeight);
            mToolbar.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, mColorThemePrimary));
            mToolbar.setTitleTextColor(ScrollUtils.getColorWithAlpha(alpha, mColorTextWhite));
        }
    }

    @Override
    public void onDownMotionEvent() {
        /** ignore */
    }

    @Override
    public void onUpOrCancelMotionEvent(ScrollState scrollState) {
        /** ignore */
    }

    private void trySetupToolbar() {
        if (getActivity() instanceof MovieDetailsActivity) {
            MovieDetailsActivity activity = ((MovieDetailsActivity) getActivity());
            mToolbar = activity.getToolbar();
        }
    }

    private void onMovieLoaded(Movie movie) {
        mMovie = movie;

        if (mToolbar != null) {
            mToolbar.setTitle(mMovie.getTitle());
        }

        mTitle.setText(movie.getTitle());
        mRating.setText(getString(R.string.movie_details_rating, movie.getVoteAverage()));
        mReleaseDate.setText(UiUtils.getDisplayReleaseDate(movie.getReleaseDate()));
        mOverview.setText(movie.getOverview());
        mFavoriteButton.setSelected(movie.isFavored());

        // Cover image
        Glide.with(this).load(movie.getBackdropPath()).placeholder(R.color.movie_cover_placeholder).centerCrop()
                .crossFade().into(mCoverImage);

        // Poster image
        Glide.with(this).load(movie.getPosterPath()).centerCrop().crossFade().into(mPosterImage);
    }

    private void loadReviews() {
        mSubscriptions.add(mMoviesRepository.reviews(mMovie.getId()).subscribe(reviews -> {
            Timber.d(String.format("Reviews loaded, %d items.", reviews.size()));
            onReviewsLoaded(reviews);
        }, throwable -> {
            Timber.e(throwable, "Reviews loading failed.");
            onReviewsLoaded(null);
        }));
    }

    private void onReviewsLoaded(List<Review> reviews) {
        mReviews = reviews;
        // Remove all existing reviews (everything but first two children)
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                for (int i = mReviewsGroup.getChildCount() - 1; i >= 2; i--) {
                    mReviewsGroup.removeViewAt(i);
                }
            }
        });

        final LayoutInflater inflater = LayoutInflater.from(getActivity());
        boolean hasReviews = false;

        if (!Lists.isEmpty(reviews)) {
            for (Review review : reviews) {
                if (TextUtils.isEmpty(review.getAuthor())) {
                    continue;
                }

                final View reviewView = inflater.inflate(R.layout.item_review_detail, mReviewsGroup, false);
                final TextView reviewAuthorView = findById(reviewView, R.id.review_author);
                final TextView reviewContentView = findById(reviewView, R.id.review_content);

                reviewAuthorView.setText(review.getAuthor());
                reviewContentView.setText(review.getContent());

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mReviewsGroup.addView(reviewView);
                    }
                });

                hasReviews = true;
            }
        }

        boolean finalHasReviews = hasReviews;
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mReviewsGroup.setVisibility(finalHasReviews ? View.VISIBLE : View.GONE);
            }
        });

    }

    private void loadVideos() {
        mSubscriptions.add(mMoviesRepository.videos(mMovie.getId()).subscribe(videos -> {
            Timber.d(String.format("Videos loaded, %d items.", videos.size()));
            Timber.d("Videos: " + videos);
            onVideosLoaded(videos);
        }, throwable -> {
            Timber.e(throwable, "Videos loading failed.");
            onVideosLoaded(null);
        }));
    }

    private void onVideosLoaded(List<Video> videos) {
        mVideos = videos;

        // Remove all existing videos (everything but first two children)
        for (int i = mVideosGroup.getChildCount() - 1; i >= 2; i--) {
            mVideosGroup.removeViewAt(i);
        }

        final LayoutInflater inflater = LayoutInflater.from(getActivity());

        boolean hasVideos = false;
        if (!Lists.isEmpty(videos)) {
            for (Video video : mVideos)
                if (video.getType().equals(Video.TYPE_TRAILER)) {
                    Timber.d("Found trailer!");
                    mTrailer = video;

                    mCoverContainer.setTag(video);
                    mCoverContainer.setOnClickListener(view -> mHelper.playVideo((Video) view.getTag()));
                    break;
                }

            for (Video video : videos) {
                final View videoView = inflater.inflate(R.layout.item_video, mVideosGroup, false);
                final TextView videoNameView = findById(videoView, R.id.video_name);

                videoNameView.setText(video.getSite() + ": " + video.getName());
                videoView.setTag(video);
                videoView.setOnClickListener(v -> {
                    mHelper.playVideo((Video) v.getTag());
                });

                getActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mVideosGroup.addView(videoView);
                    }
                });

                hasVideos = true;
            }
        }

        boolean finalHasVideos = hasVideos;
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                showShareMenuItemDeferred(mTrailer != null);
                mCoverContainer.setClickable(mTrailer != null);
                mPosterPlayImage.setVisibility(mTrailer != null ? View.VISIBLE : View.GONE);
                mVideosGroup.setVisibility(finalHasVideos ? View.VISIBLE : View.GONE);
            }
        });

    }

    private void showShareMenuItemDeferred(boolean visible) {
        mDeferredUiOperations.add(() -> mMenuItemShare.setVisible(visible));
        tryExecuteDeferredUiOperations();
    }

    private void tryExecuteDeferredUiOperations() {
        if (mMenuItemShare != null) {
            for (Runnable r : mDeferredUiOperations) {
                r.run();
            }
            mDeferredUiOperations.clear();
        }
    }

    @Override
    protected List<Object> getModules() {
        return Collections.<Object>singletonList(new MoviesModule());
    }

}