/* * Copyright (C) 2016 Simon Vig Therkildsen * * 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 net.simonvt.cathode.ui.movie; import android.app.Activity; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import butterknife.BindView; import butterknife.OnClick; import java.util.Locale; import javax.inject.Inject; import net.simonvt.cathode.CathodeApp; import net.simonvt.cathode.R; import net.simonvt.cathode.api.enumeration.ItemType; import net.simonvt.cathode.api.util.TraktUtils; import net.simonvt.cathode.database.SimpleCursor; import net.simonvt.cathode.database.SimpleCursorLoader; import net.simonvt.cathode.images.ImageType; import net.simonvt.cathode.images.ImageUri; import net.simonvt.cathode.jobqueue.Job; import net.simonvt.cathode.provider.DatabaseContract; import net.simonvt.cathode.provider.DatabaseContract.CommentColumns; import net.simonvt.cathode.provider.DatabaseContract.MovieCastColumns; import net.simonvt.cathode.provider.DatabaseContract.MovieColumns; import net.simonvt.cathode.provider.DatabaseContract.MovieGenreColumns; import net.simonvt.cathode.provider.DatabaseContract.PersonColumns; import net.simonvt.cathode.provider.DatabaseContract.RelatedMoviesColumns; import net.simonvt.cathode.provider.DatabaseContract.UserColumns; import net.simonvt.cathode.provider.DatabaseSchematic.Tables; import net.simonvt.cathode.provider.ProviderSchematic; import net.simonvt.cathode.provider.ProviderSchematic.Comments; import net.simonvt.cathode.provider.ProviderSchematic.MovieGenres; import net.simonvt.cathode.provider.ProviderSchematic.Movies; import net.simonvt.cathode.provider.ProviderSchematic.RelatedMovies; import net.simonvt.cathode.scheduler.MovieTaskScheduler; import net.simonvt.cathode.scheduler.PersonTaskScheduler; import net.simonvt.cathode.settings.TraktTimestamps; import net.simonvt.cathode.ui.NavigationListener; import net.simonvt.cathode.ui.comments.LinearCommentsAdapter; import net.simonvt.cathode.ui.dialog.CheckInDialog; import net.simonvt.cathode.ui.dialog.CheckInDialog.Type; import net.simonvt.cathode.ui.dialog.RatingDialog; import net.simonvt.cathode.ui.fragment.RefreshableAppBarFragment; import net.simonvt.cathode.ui.lists.ListsDialog; import net.simonvt.cathode.util.Ids; import net.simonvt.cathode.util.Intents; import net.simonvt.cathode.util.SqlColumn; import net.simonvt.cathode.widget.CheckInDrawable; import net.simonvt.cathode.widget.CircleTransformation; import net.simonvt.cathode.widget.CircularProgressIndicator; import net.simonvt.cathode.widget.RemoteImageView; import net.simonvt.schematic.Cursors; public class MovieFragment extends RefreshableAppBarFragment implements LoaderManager.LoaderCallbacks<SimpleCursor> { private static final String TAG = "net.simonvt.cathode.ui.movie.MovieFragment"; private static final int LOADER_MOVIE = 1; private static final int LOADER_MOVIE_GENRES = 2; private static final int LOADER_MOVIE_CAST = 3; private static final int LOADER_MOVIE_USER_COMMENTS = 4; private static final int LOADER_MOVIE_COMMENTS = 5; private static final int LOADER_RELATED = 6; private static final String ARG_ID = "net.simonvt.cathode.ui.movie.MovieFragment.id"; private static final String ARG_TITLE = "net.simonvt.cathode.ui.movie.MovieFragment.title"; private static final String ARG_OVERVIEW = "net.simonvt.cathode.ui.movie.MovieFragment.overview"; private static final String DIALOG_RATING = "net.simonvt.cathode.ui.movie.MovieFragment.ratingDialog"; private static final String DIALOG_LISTS_ADD = "net.simonvt.cathode.ui.movie.MovieFragment.listsAddDialog"; private static final String DIALOG_COMMENT_ADD = "net.simonvt.cathode.ui.movie.MovieFragment.addCommentDialog"; private static final String DIALOG_COMMENT_UPDATE = "net.simonvt.cathode.ui.movie.MovieFragment.updateCommentDialog"; @Inject MovieTaskScheduler movieScheduler; @Inject PersonTaskScheduler personScheduler; @BindView(R.id.year) TextView year; @BindView(R.id.certification) TextView certification; //@BindView(R.id.poster) RemoteImageView poster; @BindView(R.id.overview) TextView overview; @BindView(R.id.genresTitle) View genresTitle; @BindView(R.id.genres) TextView genres; @BindView(R.id.isWatched) TextView isWatched; @BindView(R.id.inCollection) TextView collection; @BindView(R.id.inWatchlist) TextView watchlist; @BindView(R.id.rating) CircularProgressIndicator rating; @BindView(R.id.trailer) View trailer; @BindView(R.id.castParent) View castParent; @BindView(R.id.castHeader) View castHeader; @BindView(R.id.cast) LinearLayout cast; @BindView(R.id.castContainer) LinearLayout castContainer; @BindView(R.id.commentsParent) View commentsParent; @BindView(R.id.commentsHeader) View commentsHeader; @BindView(R.id.commentsContainer) LinearLayout commentsContainer; @BindView(R.id.relatedParent) View relatedParent; @BindView(R.id.relatedHeader) View relatedHeader; @BindView(R.id.related) LinearLayout related; @BindView(R.id.relatedContainer) LinearLayout relatedContainer; @BindView(R.id.websiteTitle) View websiteTitle; @BindView(R.id.website) TextView website; @BindView(R.id.viewOnTrakt) View viewOnTrakt; @BindView(R.id.viewOnImdb) View viewOnImdb; @BindView(R.id.viewOnTmdb) View viewOnTmdb; private Cursor userComments; private Cursor comments; private long movieId; private String movieTitle; private String movieOverview; private int currentRating; private boolean loaded; private boolean watched; private boolean collected; private boolean inWatchlist; private boolean watching; private boolean checkedIn; private NavigationListener navigationListener; private MenuItem checkInItem; private CheckInDrawable checkInDrawable; public static String getTag(long movieId) { return TAG + "/" + movieId + "/" + Ids.newId(); } public static Bundle getArgs(long movieId, String movieTitle, String overview) { if (movieId < 0) { throw new IllegalArgumentException("movieId must be >= 0"); } Bundle args = new Bundle(); args.putLong(ARG_ID, movieId); args.putString(ARG_TITLE, movieTitle); args.putString(ARG_OVERVIEW, overview); return args; } @Override public void onAttach(Activity activity) { super.onAttach(activity); navigationListener = (NavigationListener) activity; } @Override public void onCreate(Bundle inState) { super.onCreate(inState); CathodeApp.inject(getActivity(), this); Bundle args = getArguments(); movieId = args.getLong(ARG_ID); movieTitle = args.getString(ARG_TITLE); movieOverview = args.getString(ARG_OVERVIEW); setTitle(movieTitle); } public long getMovieId() { return movieId; } @Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle inState) { return inflater.inflate(R.layout.fragment_movie, container, false); } @Override public void onViewCreated(View view, Bundle inState) { super.onViewCreated(view, inState); overview.setText(movieOverview); getLoaderManager().initLoader(LOADER_MOVIE, null, this); getLoaderManager().initLoader(LOADER_MOVIE_GENRES, null, genresLoader); getLoaderManager().initLoader(LOADER_MOVIE_CAST, null, castLoader); getLoaderManager().initLoader(LOADER_MOVIE_USER_COMMENTS, null, userCommentsLoader); getLoaderManager().initLoader(LOADER_MOVIE_COMMENTS, null, commentsLoader); getLoaderManager().initLoader(LOADER_RELATED, null, relatedLoader); } @Override public void onDestroyView() { checkInItem = null; super.onDestroyView(); } @OnClick(R.id.rating) void onRatingClick() { RatingDialog.newInstance(RatingDialog.Type.MOVIE, movieId, currentRating) .show(getFragmentManager(), DIALOG_RATING); } @OnClick(R.id.castHeader) void onDisplayCast() { navigationListener.onDisplayCredits(ItemType.MOVIE, movieId, movieTitle); } @OnClick(R.id.commentsHeader) void onShowComments() { navigationListener.onDisplayComments(ItemType.MOVIE, movieId); } @OnClick(R.id.relatedHeader) void onShowRelated() { navigationListener.onDisplayRelatedMovies(movieId, movieTitle); } private Job.OnDoneListener onDoneListener = new Job.OnDoneListener() { @Override public void onDone(Job job) { setRefreshing(false); } }; @Override public void onRefresh() { movieScheduler.sync(movieId, onDoneListener); } @Override public void createMenu(Toolbar toolbar) { if (loaded) { Menu menu = toolbar.getMenu(); if (menu.findItem(R.id.menu_lists_add) == null) { menu.add(0, R.id.menu_lists_add, 0, R.string.action_list_add); } if (checkInDrawable == null) { checkInDrawable = new CheckInDrawable(toolbar.getContext()); checkInDrawable.setWatching(watching || checkedIn); checkInDrawable.setId(movieId); } if (checkInItem == null) { if (watching || checkedIn) { checkInItem = menu.add(0, R.id.action_checkin, 1, R.string.action_checkin_cancel); } else { checkInItem = menu.add(0, R.id.action_checkin, 1, R.string.action_checkin); } checkInItem.setIcon(checkInDrawable).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } else { if (watching || checkedIn) { checkInItem.setTitle(R.string.action_checkin_cancel); } else { checkInItem.setTitle(R.string.action_checkin); } } if (watching) { checkInItem.setEnabled(false); } else { checkInItem.setEnabled(true); } menu.removeItem(R.id.action_unwatched); menu.removeItem(R.id.action_watched); menu.removeItem(R.id.action_watchlist_remove); menu.removeItem(R.id.action_watchlist_add); menu.removeItem(R.id.action_collection_remove); menu.removeItem(R.id.action_collection_add); if (watched) { menu.add(0, R.id.action_unwatched, 3, R.string.action_unwatched); } else { menu.add(0, R.id.action_watched, 4, R.string.action_watched); if (inWatchlist) { menu.add(0, R.id.action_watchlist_remove, 7, R.string.action_watchlist_remove); } else { menu.add(0, R.id.action_watchlist_add, 8, R.string.action_watchlist_add); } } if (collected) { menu.add(0, R.id.action_collection_remove, 5, R.string.action_collection_remove); } else { menu.add(0, R.id.action_collection_add, 6, R.string.action_collection_add); } } } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_watched: movieScheduler.setWatched(movieId, true); return true; case R.id.action_unwatched: movieScheduler.setWatched(movieId, false); return true; case R.id.action_checkin: if (!watching) { if (checkedIn) { movieScheduler.cancelCheckin(); if (checkInDrawable != null) { checkInDrawable.setWatching(false); } } else { if (!CheckInDialog.showDialogIfNecessary(getActivity(), Type.MOVIE, movieTitle, movieId)) { checkInDrawable.setWatching(true); } } } return true; case R.id.action_checkin_cancel: movieScheduler.cancelCheckin(); return true; case R.id.action_watchlist_add: movieScheduler.setIsInWatchlist(movieId, true); return true; case R.id.action_watchlist_remove: movieScheduler.setIsInWatchlist(movieId, false); return true; case R.id.action_collection_add: movieScheduler.setIsInCollection(movieId, true); return true; case R.id.action_collection_remove: movieScheduler.setIsInCollection(movieId, false); return true; case R.id.menu_lists_add: ListsDialog.newInstance(DatabaseContract.ItemType.MOVIE, movieId) .show(getFragmentManager(), DIALOG_LISTS_ADD); return true; } return super.onMenuItemClick(item); } private void updateView(final Cursor cursor) { if (cursor == null || !cursor.moveToFirst()) return; loaded = true; final long traktId = Cursors.getLong(cursor, MovieColumns.TRAKT_ID); final String title = Cursors.getString(cursor, MovieColumns.TITLE); if (title != null && !title.equals(movieTitle)) { movieTitle = title; setTitle(movieTitle); } final int year = Cursors.getInt(cursor, MovieColumns.YEAR); final String certification = Cursors.getString(cursor, MovieColumns.CERTIFICATION); final String backdropUri = ImageUri.create(ImageUri.ITEM_MOVIE, ImageType.BACKDROP, movieId); setBackdrop(backdropUri, true); currentRating = Cursors.getInt(cursor, MovieColumns.USER_RATING); final float ratingAll = Cursors.getFloat(cursor, MovieColumns.RATING); rating.setValue(ratingAll); movieOverview = Cursors.getString(cursor, MovieColumns.OVERVIEW); watched = Cursors.getBoolean(cursor, MovieColumns.WATCHED); collected = Cursors.getBoolean(cursor, MovieColumns.IN_COLLECTION); inWatchlist = Cursors.getBoolean(cursor, MovieColumns.IN_WATCHLIST); watching = Cursors.getBoolean(cursor, MovieColumns.WATCHING); checkedIn = Cursors.getBoolean(cursor, MovieColumns.CHECKED_IN); if (checkInDrawable != null) { checkInDrawable.setWatching(watching || checkedIn); } isWatched.setVisibility(watched ? View.VISIBLE : View.GONE); collection.setVisibility(collected ? View.VISIBLE : View.GONE); watchlist.setVisibility(inWatchlist ? View.VISIBLE : View.GONE); this.year.setText(String.valueOf(year)); this.certification.setText(certification); this.overview.setText(movieOverview); final String trailer = Cursors.getString(cursor, MovieColumns.TRAILER); if (!TextUtils.isEmpty(trailer)) { this.trailer.setVisibility(View.VISIBLE); this.trailer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.openUrl(getActivity(), trailer); } }); } else { this.trailer.setVisibility(View.GONE); } final boolean needsSync = Cursors.getBoolean(cursor, MovieColumns.NEEDS_SYNC); if (needsSync) { movieScheduler.sync(movieId); } final long lastSync = Cursors.getLong(cursor, MovieColumns.LAST_SYNC); final long lastCommentSync = Cursors.getLong(cursor, MovieColumns.LAST_COMMENT_SYNC); if (TraktTimestamps.shouldSyncComments(lastCommentSync)) { movieScheduler.syncComments(movieId); } final long lastCreditsSync = Cursors.getLong(cursor, MovieColumns.LAST_CREDITS_SYNC); if (lastSync > lastCreditsSync) { movieScheduler.syncCredits(movieId, null); } final long lastRelatedSync = Cursors.getLong(cursor, MovieColumns.LAST_RELATED_SYNC); if (lastSync > lastRelatedSync) { movieScheduler.syncRelated(movieId, null); } final String website = Cursors.getString(cursor, MovieColumns.HOMEPAGE); if (!TextUtils.isEmpty(website)) { this.websiteTitle.setVisibility(View.VISIBLE); this.website.setVisibility(View.VISIBLE); this.website.setText(website); this.website.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.openUrl(getContext(), website); } }); } else { this.websiteTitle.setVisibility(View.GONE); this.website.setVisibility(View.GONE); } final String imdbId = Cursors.getString(cursor, MovieColumns.IMDB_ID); final int tmdbId = Cursors.getInt(cursor, MovieColumns.TMDB_ID); viewOnTrakt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.openUrl(getContext(), TraktUtils.getTraktMovieUrl(traktId)); } }); final boolean hasImdbId = !TextUtils.isEmpty(imdbId); if (hasImdbId) { viewOnImdb.setVisibility(View.VISIBLE); viewOnImdb.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.openUrl(getContext(), TraktUtils.getImdbUrl(imdbId)); } }); } else { viewOnImdb.setVisibility(View.GONE); } if (tmdbId > 0) { viewOnTmdb.setVisibility(View.VISIBLE); viewOnTmdb.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.openUrl(getContext(), TraktUtils.getTmdbMovieUrl(tmdbId)); } }); } else { viewOnTmdb.setVisibility(View.GONE); } if (getToolbar() != null) { createMenu(getToolbar()); } } @Override public Loader<SimpleCursor> onCreateLoader(int i, Bundle bundle) { return new SimpleCursorLoader(getActivity(), Movies.withId(movieId), null, null, null, null); } @Override public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor cursor) { updateView(cursor); } @Override public void onLoaderReset(Loader<SimpleCursor> cursorLoader) { } private void updateGenreViews(final Cursor cursor) { if (cursor.getCount() > 0) { StringBuilder sb = new StringBuilder(); final int genreColumnIndex = cursor.getColumnIndex(MovieGenreColumns.GENRE); cursor.moveToPosition(-1); while (cursor.moveToNext()) { sb.append(cursor.getString(genreColumnIndex)); if (!cursor.isLast()) sb.append(", "); } genresTitle.setVisibility(View.VISIBLE); genres.setVisibility(View.VISIBLE); genres.setText(sb.toString()); } else { genresTitle.setVisibility(View.GONE); genres.setVisibility(View.GONE); } } private void updateCast(Cursor c) { castContainer.removeAllViews(); final int count = c.getCount(); final int visibility = count > 0 ? View.VISIBLE : View.GONE; castParent.setVisibility(visibility); int index = 0; c.moveToPosition(-1); while (c.moveToNext() && index < 3) { View v = LayoutInflater.from(getActivity()).inflate(R.layout.item_person, castContainer, false); final long personId = Cursors.getLong(c, MovieCastColumns.PERSON_ID); final String headshotUri = ImageUri.create(ImageUri.ITEM_PERSON, ImageType.PROFILE, personId); RemoteImageView headshot = (RemoteImageView) v.findViewById(R.id.headshot); headshot.addTransformation(new CircleTransformation()); headshot.setImage(headshotUri); TextView name = (TextView) v.findViewById(R.id.person_name); name.setText(Cursors.getString(c, PersonColumns.NAME)); TextView character = (TextView) v.findViewById(R.id.person_job); character.setText(Cursors.getString(c, MovieCastColumns.CHARACTER)); v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { navigationListener.onDisplayPerson(personId); } }); castContainer.addView(v); index++; } } private void updateRelatedView(Cursor related) { relatedContainer.removeAllViews(); final int count = related.getCount(); final int visibility = count > 0 ? View.VISIBLE : View.GONE; relatedParent.setVisibility(visibility); int index = 0; related.moveToPosition(-1); while (related.moveToNext() && index < 3) { View v = LayoutInflater.from(getActivity()) .inflate(R.layout.item_related, relatedContainer, false); final long relatedMovieId = Cursors.getLong(related, RelatedMoviesColumns.RELATED_MOVIE_ID); final String title = Cursors.getString(related, MovieColumns.TITLE); final String overview = Cursors.getString(related, MovieColumns.OVERVIEW); final float rating = Cursors.getFloat(related, MovieColumns.RATING); final int votes = Cursors.getInt(related, MovieColumns.VOTES); final String poster = ImageUri.create(ImageUri.ITEM_MOVIE, ImageType.POSTER, relatedMovieId); RemoteImageView posterView = (RemoteImageView) v.findViewById(R.id.related_poster); posterView.addTransformation(new CircleTransformation()); posterView.setImage(poster); TextView titleView = (TextView) v.findViewById(R.id.related_title); titleView.setText(title); final String formattedRating = String.format(Locale.getDefault(), "%.1f", rating); String ratingText; if (votes >= 1000) { final float convertedVotes = votes / 1000.0f; final String formattedVotes = String.format(Locale.getDefault(), "%.1f", convertedVotes); ratingText = getString(R.string.related_rating_thousands, formattedRating, formattedVotes); } else { ratingText = getString(R.string.related_rating, formattedRating, votes); } TextView ratingView = (TextView) v.findViewById(R.id.related_rating); ratingView.setText(ratingText); v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { navigationListener.onDisplayMovie(relatedMovieId, title, overview); } }); relatedContainer.addView(v); index++; } } private void updateComments() { LinearCommentsAdapter.updateComments(getContext(), commentsContainer, userComments, comments); commentsParent.setVisibility(View.VISIBLE); } private static final String[] GENRES_PROJECTION = new String[] { MovieGenreColumns.GENRE, }; private LoaderManager.LoaderCallbacks<SimpleCursor> genresLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() { @Override public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) { return new SimpleCursorLoader(getActivity(), MovieGenres.fromMovie(movieId), GENRES_PROJECTION, null, null, null); } @Override public void onLoadFinished(Loader<SimpleCursor> loader, SimpleCursor data) { updateGenreViews(data); } @Override public void onLoaderReset(Loader<SimpleCursor> loader) { } }; private static final String[] CAST_PROJECTION = new String[] { Tables.MOVIE_CAST + "." + MovieCastColumns.ID, Tables.MOVIE_CAST + "." + MovieCastColumns.PERSON_ID, Tables.MOVIE_CAST + "." + MovieCastColumns.CHARACTER, Tables.PEOPLE + "." + PersonColumns.NAME, }; private LoaderManager.LoaderCallbacks<SimpleCursor> castLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() { @Override public Loader<SimpleCursor> onCreateLoader(int i, Bundle bundle) { return new SimpleCursorLoader(getActivity(), ProviderSchematic.MovieCast.fromMovie(movieId), CAST_PROJECTION, Tables.PEOPLE + "." + PersonColumns.NEEDS_SYNC + "=0", null, null); } @Override public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor cursor) { updateCast(cursor); } @Override public void onLoaderReset(Loader<SimpleCursor> cursorLoader) { } }; private static final String[] COMMENTS_PROJECTION = new String[] { SqlColumn.table(Tables.COMMENTS).column(CommentColumns.ID), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.COMMENT), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.SPOILER), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.REVIEW), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.CREATED_AT), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.LIKES), SqlColumn.table(Tables.COMMENTS).column(CommentColumns.USER_RATING), SqlColumn.table(Tables.USERS).column(UserColumns.USERNAME), SqlColumn.table(Tables.USERS).column(UserColumns.NAME), SqlColumn.table(Tables.USERS).column(UserColumns.AVATAR), }; private LoaderManager.LoaderCallbacks<SimpleCursor> userCommentsLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() { @Override public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) { return new SimpleCursorLoader(getContext(), Comments.fromMovie(movieId), COMMENTS_PROJECTION, CommentColumns.IS_USER_COMMENT + "=1", null, null); } @Override public void onLoadFinished(Loader<SimpleCursor> loader, SimpleCursor data) { userComments = data; updateComments(); } @Override public void onLoaderReset(Loader<SimpleCursor> loader) { } }; private LoaderManager.LoaderCallbacks<SimpleCursor> commentsLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() { @Override public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) { return new SimpleCursorLoader(getContext(), Comments.fromMovie(movieId), COMMENTS_PROJECTION, CommentColumns.IS_USER_COMMENT + "=0 AND " + CommentColumns.SPOILER + "=0", null, CommentColumns.LIKES + " DESC LIMIT 3"); } @Override public void onLoadFinished(Loader<SimpleCursor> loader, SimpleCursor data) { comments = data; updateComments(); } @Override public void onLoaderReset(Loader<SimpleCursor> loader) { } }; private static final String[] RELATED_PROJECTION = new String[] { SqlColumn.table(Tables.MOVIE_RELATED).column(RelatedMoviesColumns.RELATED_MOVIE_ID), SqlColumn.table(Tables.MOVIES).column(MovieColumns.TITLE), SqlColumn.table(Tables.MOVIES).column(MovieColumns.OVERVIEW), SqlColumn.table(Tables.MOVIES).column(MovieColumns.RATING), SqlColumn.table(Tables.MOVIES).column(MovieColumns.VOTES), }; private LoaderManager.LoaderCallbacks<SimpleCursor> relatedLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() { @Override public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) { return new SimpleCursorLoader(getContext(), RelatedMovies.fromMovie(movieId), RELATED_PROJECTION, null, null, RelatedMoviesColumns.RELATED_INDEX + " ASC LIMIT 3"); } @Override public void onLoadFinished(Loader<SimpleCursor> loader, SimpleCursor data) { updateRelatedView(data); } @Override public void onLoaderReset(Loader<SimpleCursor> loader) { } }; }