/*
* 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.show;
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.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 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.EpisodeColumns;
import net.simonvt.cathode.provider.DatabaseContract.UserColumns;
import net.simonvt.cathode.provider.DatabaseSchematic.Tables;
import net.simonvt.cathode.provider.ProviderSchematic.Comments;
import net.simonvt.cathode.provider.ProviderSchematic.Episodes;
import net.simonvt.cathode.scheduler.EpisodeTaskScheduler;
import net.simonvt.cathode.scheduler.ShowTaskScheduler;
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.DataHelper;
import net.simonvt.cathode.util.DateUtils;
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.CircularProgressIndicator;
import net.simonvt.schematic.Cursors;
public class EpisodeFragment extends RefreshableAppBarFragment {
private static final String TAG = "net.simonvt.cathode.ui.show.EpisodeFragment";
private static final int LOADER_EPISODE = 1;
private static final int LOADER_EPISODE_USER_COMMENTS = 2;
private static final String ARG_EPISODEID =
"net.simonvt.cathode.ui.show.EpisodeFragment.episodeId";
private static final String ARG_SHOW_TITLE =
"net.simonvt.cathode.ui.show.EpisodeFragment.showTitle";
private static final String DIALOG_RATING =
"net.simonvt.cathode.ui.show.EpisodeFragment.ratingDialog";
private static final String DIALOG_LISTS_ADD =
"net.simonvt.cathode.ui.show.EpisodeFragment.listsAddDialog";
private static final String DIALOG_COMMENT_ADD =
"net.simonvt.cathode.ui.show.EpisodeFragment.addCommentDialog";
private static final String DIALOG_COMMENT_UPDATE =
"net.simonvt.cathode.ui.show.EpisodeFragment.updateCommentDialog";
@Inject ShowTaskScheduler showScheduler;
@Inject EpisodeTaskScheduler episodeScheduler;
@BindView(R.id.title) TextView title;
@BindView(R.id.overview) TextView overview;
@BindView(R.id.firstAired) TextView firstAired;
@BindView(R.id.rating) CircularProgressIndicator rating;
@BindView(R.id.isWatched) View watchedView;
@BindView(R.id.inCollection) View inCollectionView;
@BindView(R.id.inWatchlist) View inWatchlistView;
@BindView(R.id.commentsParent) View commentsParent;
@BindView(R.id.commentsHeader) View commentsHeader;
@BindView(R.id.commentsContainer) LinearLayout commentsContainer;
@BindView(R.id.viewOnTrakt) View viewOnTrakt;
private Cursor userComments;
private long episodeId;
private String episodeTitle;
private String showTitle;
private long showId = -1L;
private int season = -1;
private long seasonId = -1L;
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 episodeId) {
return TAG + "/" + episodeId + "/" + Ids.newId();
}
public static Bundle getArgs(long episodeId, String showTitle) {
Bundle args = new Bundle();
args.putLong(ARG_EPISODEID, episodeId);
args.putString(ARG_SHOW_TITLE, showTitle);
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();
episodeId = args.getLong(ARG_EPISODEID);
showTitle = args.getString(ARG_SHOW_TITLE);
setTitle(showTitle);
getLoaderManager().initLoader(LOADER_EPISODE, null, episodeCallbacks);
getLoaderManager().initLoader(LOADER_EPISODE_USER_COMMENTS, null, userCommentsLoader);
}
public long getEpisodeId() {
return episodeId;
}
@Override public boolean onBackPressed() {
return false;
}
@Override protected void onHomeClicked() {
if (showId >= 0L && seasonId >= 0L) {
navigationListener.upFromEpisode(showId, showTitle, seasonId);
} else {
navigationListener.onHomeClicked();
}
}
@Override public View createView(LayoutInflater inflater, ViewGroup container, Bundle inState) {
return inflater.inflate(R.layout.fragment_episode, container, false);
}
private Job.OnDoneListener onDoneListener = new Job.OnDoneListener() {
@Override public void onDone(Job job) {
setRefreshing(false);
}
};
@Override public void onRefresh() {
episodeScheduler.sync(episodeId, onDoneListener);
}
@OnClick(R.id.rating) void onRatingClick() {
RatingDialog.newInstance(RatingDialog.Type.EPISODE, episodeId, currentRating)
.show(getFragmentManager(), DIALOG_RATING);
}
@OnClick(R.id.commentsHeader) void onShowComments() {
navigationListener.onDisplayComments(ItemType.EPISODE, episodeId);
}
@Override public void onDestroyView() {
checkInItem = null;
super.onDestroyView();
}
@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(episodeId);
}
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, 5, R.string.action_watchlist_remove);
} else {
menu.add(0, R.id.action_watchlist_add, 6, R.string.action_watchlist_add);
}
}
if (collected) {
menu.add(0, R.id.action_collection_remove, 7, R.string.action_collection_remove);
} else {
menu.add(0, R.id.action_collection_add, 8, R.string.action_collection_add);
}
}
}
@Override public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_watched:
episodeScheduler.setWatched(episodeId, true);
return true;
case R.id.action_unwatched:
episodeScheduler.setWatched(episodeId, false);
return true;
case R.id.action_checkin:
if (!watching) {
if (checkedIn) {
showScheduler.cancelCheckin();
if (checkInDrawable != null) {
checkInDrawable.setWatching(false);
}
} else {
if (!CheckInDialog.showDialogIfNecessary(getActivity(), Type.SHOW, episodeTitle,
episodeId)) {
checkInDrawable.setWatching(true);
}
}
}
return true;
case R.id.action_checkin_cancel:
showScheduler.cancelCheckin();
return true;
case R.id.action_collection_add:
episodeScheduler.setIsInCollection(episodeId, true);
return true;
case R.id.action_collection_remove:
episodeScheduler.setIsInCollection(episodeId, false);
return true;
case R.id.action_watchlist_add:
episodeScheduler.setIsInWatchlist(episodeId, true);
return true;
case R.id.action_watchlist_remove:
episodeScheduler.setIsInWatchlist(episodeId, false);
return true;
case R.id.menu_lists_add:
ListsDialog.newInstance(DatabaseContract.ItemType.EPISODE, episodeId)
.show(getFragmentManager(), DIALOG_LISTS_ADD);
return true;
default:
return super.onMenuItemClick(item);
}
}
private void updateEpisodeViews(final Cursor cursor) {
if (cursor == null || !cursor.moveToFirst()) return;
loaded = true;
final long traktId = Cursors.getLong(cursor, EpisodeColumns.TRAKT_ID);
showId = Cursors.getLong(cursor, EpisodeColumns.SHOW_ID);
seasonId = Cursors.getLong(cursor, EpisodeColumns.SEASON_ID);
season = Cursors.getInt(cursor, EpisodeColumns.SEASON);
final int episode = Cursors.getInt(cursor, EpisodeColumns.EPISODE);
episodeTitle = DataHelper.getEpisodeTitle(getContext(), cursor, season, episode);
title.setText(episodeTitle);
overview.setText(Cursors.getString(cursor, EpisodeColumns.OVERVIEW));
final String screenshotUri = ImageUri.create(ImageUri.ITEM_EPISODE, ImageType.STILL, episodeId);
setBackdrop(screenshotUri, true);
firstAired.setText(
DateUtils.millisToString(getActivity(), DataHelper.getFirstAired(cursor), true));
watched = Cursors.getBoolean(cursor, EpisodeColumns.WATCHED);
collected = Cursors.getBoolean(cursor, EpisodeColumns.IN_COLLECTION);
inWatchlist = Cursors.getBoolean(cursor, EpisodeColumns.IN_WATCHLIST);
watching = Cursors.getBoolean(cursor, EpisodeColumns.WATCHING);
checkedIn = Cursors.getBoolean(cursor, EpisodeColumns.CHECKED_IN);
if (checkInDrawable != null) {
checkInDrawable.setWatching(watching || checkedIn);
}
watchedView.setVisibility(watched ? View.VISIBLE : View.GONE);
inCollectionView.setVisibility(collected ? View.VISIBLE : View.GONE);
inWatchlistView.setVisibility(inWatchlist ? View.VISIBLE : View.GONE);
currentRating = Cursors.getInt(cursor, EpisodeColumns.USER_RATING);
final float ratingAll = Cursors.getFloat(cursor, EpisodeColumns.RATING);
rating.setValue(ratingAll);
viewOnTrakt.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
Intents.openUrl(getContext(), TraktUtils.getTraktEpisodeUrl(traktId));
}
});
final long lastCommentSync = Cursors.getLong(cursor, EpisodeColumns.LAST_COMMENT_SYNC);
if (TraktTimestamps.shouldSyncComments(lastCommentSync)) {
episodeScheduler.syncComments(episodeId);
}
if (getToolbar() != null) {
createMenu(getToolbar());
}
}
private void updateComments() {
LinearCommentsAdapter.updateComments(getContext(), commentsContainer, userComments, null);
commentsParent.setVisibility(View.VISIBLE);
}
private static final String[] EPISODE_PROJECTION = new String[] {
EpisodeColumns.TRAKT_ID, EpisodeColumns.TITLE, EpisodeColumns.OVERVIEW,
EpisodeColumns.FIRST_AIRED, EpisodeColumns.WATCHED, EpisodeColumns.IN_COLLECTION,
EpisodeColumns.IN_WATCHLIST, EpisodeColumns.WATCHING, EpisodeColumns.CHECKED_IN,
EpisodeColumns.USER_RATING, EpisodeColumns.RATING, EpisodeColumns.SEASON,
EpisodeColumns.EPISODE, EpisodeColumns.LAST_COMMENT_SYNC, EpisodeColumns.SHOW_ID,
EpisodeColumns.SEASON_ID,
};
private LoaderManager.LoaderCallbacks<SimpleCursor> episodeCallbacks =
new LoaderManager.LoaderCallbacks<SimpleCursor>() {
@Override public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) {
return new SimpleCursorLoader(getActivity(), Episodes.withId(episodeId),
EPISODE_PROJECTION, null, null, null);
}
@Override public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor data) {
updateEpisodeViews(data);
}
@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.fromEpisode(episodeId),
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) {
userComments = data;
updateComments();
}
@Override public void onLoaderReset(Loader<SimpleCursor> loader) {
}
};
}