net.simonvt.cathode.ui.fragment.ShowFragment.java Source code

Java tutorial

Introduction

Here is the source code for net.simonvt.cathode.ui.fragment.ShowFragment.java

Source

/*
 * Copyright (C) 2013 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.fragment;

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.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
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.ButterKnife;
import butterknife.InjectView;
import butterknife.Optional;
import com.squareup.otto.Bus;
import javax.inject.Inject;
import net.simonvt.cathode.CathodeApp;
import net.simonvt.cathode.R;
import net.simonvt.cathode.database.SimpleCursor;
import net.simonvt.cathode.database.SimpleCursorLoader;
import net.simonvt.cathode.database.SimpleMergeCursor;
import net.simonvt.cathode.provider.CollectLoader;
import net.simonvt.cathode.provider.DatabaseContract;
import net.simonvt.cathode.provider.DatabaseContract.EpisodeColumns;
import net.simonvt.cathode.provider.DatabaseContract.PersonColumns;
import net.simonvt.cathode.provider.DatabaseContract.ShowCharacterColumns;
import net.simonvt.cathode.provider.DatabaseContract.ShowColumns;
import net.simonvt.cathode.provider.DatabaseContract.ShowGenreColumns;
import net.simonvt.cathode.provider.DatabaseSchematic.Tables;
import net.simonvt.cathode.provider.ProviderSchematic;
import net.simonvt.cathode.provider.ProviderSchematic.Seasons;
import net.simonvt.cathode.provider.ProviderSchematic.ShowGenres;
import net.simonvt.cathode.provider.ProviderSchematic.Shows;
import net.simonvt.cathode.provider.WatchedLoader;
import net.simonvt.cathode.scheduler.EpisodeTaskScheduler;
import net.simonvt.cathode.scheduler.ShowTaskScheduler;
import net.simonvt.cathode.ui.LibraryType;
import net.simonvt.cathode.ui.Loaders;
import net.simonvt.cathode.ui.ShowsNavigationListener;
import net.simonvt.cathode.ui.adapter.SeasonsAdapter;
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.util.DateUtils;
import net.simonvt.cathode.widget.CircularProgressIndicator;
import net.simonvt.cathode.widget.HiddenPaneLayout;
import net.simonvt.cathode.widget.ObservableScrollView;
import net.simonvt.cathode.widget.OverflowView;
import net.simonvt.cathode.widget.RecyclerViewManager;
import net.simonvt.cathode.widget.RemoteImageView;
import timber.log.Timber;

public class ShowFragment extends ProgressFragment {

    private static final String ARG_SHOWID = "net.simonvt.cathode.ui.fragment.ShowFragment.showId";
    private static final String ARG_TITLE = "net.simonvt.cathode.ui.fragment.ShowFragment.title";
    private static final String ARG_TYPE = "net.simonvt.cathode.ui.fragment.ShowFragment.type";

    private static final String DIALOG_RATING = "net.simonvt.cathode.ui.fragment.ShowFragment.ratingDialog";

    private static final String[] SHOW_PROJECTION = new String[] { ShowColumns.TITLE, ShowColumns.YEAR,
            ShowColumns.AIR_TIME, ShowColumns.AIR_DAY, ShowColumns.NETWORK, ShowColumns.CERTIFICATION,
            ShowColumns.POSTER, ShowColumns.FANART, ShowColumns.USER_RATING, ShowColumns.RATING,
            ShowColumns.OVERVIEW, ShowColumns.IN_WATCHLIST, ShowColumns.IN_COLLECTION_COUNT,
            ShowColumns.WATCHED_COUNT, ShowColumns.HIDDEN, };

    private static final String[] EPISODE_PROJECTION = new String[] { EpisodeColumns.ID, EpisodeColumns.TITLE,
            EpisodeColumns.SCREENSHOT, EpisodeColumns.FIRST_AIRED, EpisodeColumns.SEASON, EpisodeColumns.EPISODE,
            EpisodeColumns.WATCHING, EpisodeColumns.CHECKED_IN, };

    private static final String[] GENRES_PROJECTION = new String[] { ShowGenreColumns.GENRE, };

    private ShowsNavigationListener navigationCallbacks;

    private long showId;

    @InjectView(R.id.hiddenPaneLayout)
    HiddenPaneLayout hiddenPaneLayout;

    @InjectView(R.id.seasons)
    RecyclerView seasons;
    @InjectView(R.id.seasonsEmpty)
    View seasonsEmpty;
    private SeasonsAdapter seasonsAdapter;
    private Cursor seasonsCursor;

    @InjectView(R.id.scrollView)
    ObservableScrollView scrollView;

    @InjectView(R.id.rating)
    CircularProgressIndicator rating;
    @InjectView(R.id.airtime)
    TextView airTime;
    @InjectView(R.id.certification)
    TextView certification;
    @InjectView(R.id.poster)
    RemoteImageView poster;
    @InjectView(R.id.fanart)
    RemoteImageView fanart;
    @InjectView(R.id.overview)
    TextView overview;
    @InjectView(R.id.isWatched)
    TextView watched;
    @InjectView(R.id.inCollection)
    TextView collection;
    @InjectView(R.id.inWatchlist)
    TextView watchlist;

    @InjectView(R.id.actorsTitle)
    View actorsTitle;
    @InjectView(R.id.actorsParent)
    View actorsParent;
    @InjectView(R.id.actors)
    LinearLayout actors;

    @InjectView(R.id.episodes)
    LinearLayout episodes;

    @InjectView(R.id.watchTitle)
    View watchTitle;
    @InjectView(R.id.collectTitle)
    View collectTitle;

    @InjectView(R.id.toWatch)
    View toWatch;
    private EpisodeHolder toWatchHolder;
    private long toWatchId = -1;
    private String toWatchTitle;

    @InjectView(R.id.lastWatched)
    @Optional
    View lastWatched;
    private EpisodeHolder lastWatchedHolder;
    private long lastWatchedId = -1;

    @InjectView(R.id.toCollect)
    View toCollect;
    private EpisodeHolder toCollectHolder;
    private long toCollectId = -1;

    @InjectView(R.id.lastCollected)
    @Optional
    View lastCollected;
    private EpisodeHolder lastCollectedHolder;
    private long lastCollectedId = -1;

    static class EpisodeHolder {

        @InjectView(R.id.episodeScreenshot)
        RemoteImageView episodeScreenshot;
        @InjectView(R.id.episodeTitle)
        TextView episodeTitle;
        @InjectView(R.id.episodeAirTime)
        TextView episodeAirTime;
        @InjectView(R.id.episodeEpisode)
        TextView episodeEpisode;
        @InjectView(R.id.episodeOverflow)
        OverflowView episodeOverflow;

        public EpisodeHolder(View v) {
            ButterKnife.inject(this, v);
        }
    }

    @Inject
    ShowTaskScheduler showScheduler;
    @Inject
    EpisodeTaskScheduler episodeScheduler;

    @Inject
    Bus bus;

    private String showTitle;

    private String genres;

    private boolean inWatchlist;

    private int currentRating;

    private boolean isHidden;

    private LibraryType type;

    RecyclerViewManager seasonsManager;

    public static Bundle getArgs(long showId, String title, LibraryType type) {
        Bundle args = new Bundle();
        args.putLong(ARG_SHOWID, showId);
        args.putString(ARG_TITLE, title);
        args.putSerializable(ARG_TYPE, type);
        return args;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            navigationCallbacks = (ShowsNavigationListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement ShowsNavigationListener");
        }
    }

    @Override
    public void onCreate(Bundle inState) {
        super.onCreate(inState);
        Timber.d("ShowFragment#onCreate");
        CathodeApp.inject(getActivity(), this);

        Bundle args = getArguments();
        showId = args.getLong(ARG_SHOWID);
        showTitle = args.getString(ARG_TITLE);
        type = (LibraryType) args.getSerializable(ARG_TYPE);

        seasonsAdapter = new SeasonsAdapter(getActivity(), new SeasonsAdapter.SeasonClickListener() {
            @Override
            public void onSeasonClick(View view, int position, long id) {
                navigationCallbacks.onDisplaySeason(showId, id, showTitle,
                        seasonsCursor.getInt(seasonsCursor.getColumnIndex(DatabaseContract.SeasonColumns.SEASON)),
                        type);
            }
        }, type);
    }

    private void updateTitle() {
        if (toolbar != null) {
            toolbar.setTitle(getTitle());
            toolbar.setSubtitle(getSubtitle());
        }
    }

    public String getTitle() {
        return showTitle == null ? "" : showTitle;
    }

    public String getSubtitle() {
        return genres;
    }

    @Override
    public boolean onBackPressed() {
        if (hiddenPaneLayout != null) {
            final int state = hiddenPaneLayout.getState();
            if (state == HiddenPaneLayout.STATE_OPEN || state == HiddenPaneLayout.STATE_OPENING) {
                hiddenPaneLayout.close();
                return true;
            }
        }

        return super.onBackPressed();
    }

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

    @Override
    public void onViewCreated(View view, Bundle inState) {
        super.onViewCreated(view, inState);
        seasonsManager = new RecyclerViewManager(seasons, new LinearLayoutManager(getActivity()), seasonsEmpty);
        seasonsManager.setAdapter(seasonsAdapter);
        seasons.setAdapter(seasonsAdapter);

        seasonsEmpty.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showScheduler.sync(showId);
            }
        });

        scrollView.setListener(new ObservableScrollView.ScrollListener() {
            @Override
            public void onScrollChanged(int l, int t) {
                final int offset = (int) (t / 2.0f);
                fanart.setTranslationY(offset);
            }
        });

        rating.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                RatingDialog.newInstance(RatingDialog.Type.SHOW, showId, currentRating).show(getFragmentManager(),
                        DIALOG_RATING);
            }
        });

        toWatchHolder = new EpisodeHolder(toWatch);
        toWatch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (toWatchId != -1)
                    navigationCallbacks.onDisplayEpisode(toWatchId, showTitle);
            }
        });

        toWatchHolder.episodeOverflow.setListener(new OverflowView.OverflowActionListener() {
            @Override
            public void onPopupShown() {
            }

            @Override
            public void onPopupDismissed() {
            }

            @Override
            public void onActionSelected(int action) {
                switch (action) {
                case R.id.action_checkin:
                    if (toWatchId != -1) {
                        CheckInDialog.showDialogIfNecessary(getActivity(), Type.SHOW, toWatchTitle, toWatchId);
                    }
                    break;
                case R.id.action_checkin_cancel:
                    if (toWatchId != -1) {
                        showScheduler.cancelCheckin();
                    }
                    break;
                case R.id.action_watched:
                    if (toWatchId != -1) {
                        episodeScheduler.setWatched(toWatchId, true);
                    }
                    break;
                }
            }
        });

        if (lastWatched != null) {
            lastWatchedHolder = new EpisodeHolder(lastWatched);
            lastWatched.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (lastWatchedId != -1) {
                        navigationCallbacks.onDisplayEpisode(lastWatchedId, showTitle);
                    }
                }
            });

            lastWatchedHolder.episodeOverflow.addItem(R.id.action_unwatched, R.string.action_unwatched);
            lastWatchedHolder.episodeOverflow.setListener(new OverflowView.OverflowActionListener() {
                @Override
                public void onPopupShown() {
                }

                @Override
                public void onPopupDismissed() {
                }

                @Override
                public void onActionSelected(int action) {
                    switch (action) {
                    case R.id.action_unwatched:
                        if (lastWatchedId != -1) {
                            episodeScheduler.setWatched(lastWatchedId, false);
                        }
                        break;
                    }
                }
            });
        }

        toCollectHolder = new EpisodeHolder(toCollect);
        toCollect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (toCollectId != -1)
                    navigationCallbacks.onDisplayEpisode(toCollectId, showTitle);
            }
        });

        toCollectHolder.episodeOverflow.addItem(R.id.action_collection_add, R.string.action_collection_add);
        toCollectHolder.episodeOverflow.setListener(new OverflowView.OverflowActionListener() {
            @Override
            public void onPopupShown() {
            }

            @Override
            public void onPopupDismissed() {
            }

            @Override
            public void onActionSelected(int action) {
                switch (action) {
                case R.id.action_collection_add:
                    if (toCollectId != -1) {
                        episodeScheduler.setIsInCollection(toCollectId, true);
                    }
                    break;
                }
            }
        });

        if (lastCollected != null) {
            lastCollectedHolder = new EpisodeHolder(lastCollected);
            lastCollected.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (lastCollectedId != -1) {
                        navigationCallbacks.onDisplayEpisode(lastCollectedId, showTitle);
                    }
                }
            });

            lastCollectedHolder.episodeOverflow.addItem(R.id.action_collection_remove,
                    R.string.action_collection_remove);
            lastCollectedHolder.episodeOverflow.setListener(new OverflowView.OverflowActionListener() {
                @Override
                public void onPopupShown() {
                }

                @Override
                public void onPopupDismissed() {
                }

                @Override
                public void onActionSelected(int action) {
                    switch (action) {
                    case R.id.action_collection_add:
                        if (lastCollectedId != -1) {
                            episodeScheduler.setIsInCollection(lastCollectedId, true);
                        }
                        break;
                    }
                }
            });
        }

        getLoaderManager().initLoader(Loaders.LOADER_SHOW, null, showCallbacks);
        getLoaderManager().initLoader(Loaders.LOADER_SHOW_GENRES, null, genreCallbacks);
        getLoaderManager().initLoader(Loaders.LOADER_SHOW_ACTORS, null, charactersCallback);
        getLoaderManager().initLoader(Loaders.LOADER_SHOW_WATCH, null, episodeWatchCallbacks);
        getLoaderManager().initLoader(Loaders.LOADER_SHOW_COLLECT, null, episodeCollectCallbacks);
        getLoaderManager().initLoader(Loaders.LOADER_SHOW_SEASONS, null, seasonsLoader);
    }

    @Override
    public void createMenu(Toolbar toolbar) {
        super.createMenu(toolbar);
        Menu menu = toolbar.getMenu();
        toolbar.inflateMenu(R.menu.fragment_show);

        if (inWatchlist) {
            menu.add(0, R.id.action_watchlist_remove, 300, R.string.action_watchlist_remove);
        } else {
            menu.add(0, R.id.action_watchlist_add, 300, R.string.action_watchlist_add);
        }

        if (isHidden) {
            menu.add(0, R.id.menu_show_show_upcoming, 400, R.string.action_show_show_upcoming);
        } else {
            menu.add(0, R.id.menu_show_hide_upcoming, 400, R.string.action_show_hide_upcoming);
        }
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_seasons:
            hiddenPaneLayout.toggle();
            return true;

        case R.id.menu_show_hide_upcoming:
            showScheduler.setIsHidden(showId, true);
            return true;

        case R.id.menu_show_show_upcoming:
            showScheduler.setIsHidden(showId, false);
            return true;

        case R.id.action_watchlist_remove:
            showScheduler.setIsInWatchlist(showId, false);
            return true;

        case R.id.action_watchlist_add:
            showScheduler.setIsInWatchlist(showId, true);
            return true;
        }

        return super.onMenuItemClick(item);
    }

    @Override
    public void onDestroyView() {
        scrollView.setListener(null);
        super.onDestroyView();
    }

    private void updateShowView(final Cursor cursor) {
        if (cursor == null || !cursor.moveToFirst())
            return;

        String title = cursor.getString(cursor.getColumnIndex(ShowColumns.TITLE));
        if (!TextUtils.equals(title, showTitle)) {
            showTitle = title;
            updateTitle();
        }
        final String airTime = cursor.getString(cursor.getColumnIndex(ShowColumns.AIR_TIME));
        final String airDay = cursor.getString(cursor.getColumnIndex(ShowColumns.AIR_DAY));
        final String network = cursor.getString(cursor.getColumnIndex(ShowColumns.NETWORK));
        final String certification = cursor.getString(cursor.getColumnIndex(ShowColumns.CERTIFICATION));
        final String posterUrl = cursor.getString(cursor.getColumnIndex(ShowColumns.POSTER));
        if (posterUrl != null) {
            poster.setImage(posterUrl);
        }
        final String fanartUrl = cursor.getString(cursor.getColumnIndex(ShowColumns.FANART));
        if (fanartUrl != null) {
            fanart.setImage(fanartUrl);
        }
        final String overview = cursor.getString(cursor.getColumnIndex(ShowColumns.OVERVIEW));
        inWatchlist = cursor.getInt(cursor.getColumnIndex(ShowColumns.IN_WATCHLIST)) == 1;
        final int inCollectionCount = cursor.getInt(cursor.getColumnIndex(ShowColumns.IN_COLLECTION_COUNT));
        final int watchedCount = cursor.getInt(cursor.getColumnIndex(ShowColumns.WATCHED_COUNT));
        isHidden = cursor.getInt(cursor.getColumnIndex(ShowColumns.HIDDEN)) == 1;

        currentRating = cursor.getInt(cursor.getColumnIndex(ShowColumns.USER_RATING));
        final float ratingAll = cursor.getFloat(cursor.getColumnIndex(ShowColumns.RATING));
        rating.setValue(ratingAll);

        watched.setVisibility(watchedCount > 0 ? View.VISIBLE : View.GONE);
        collection.setVisibility(inCollectionCount > 0 ? View.VISIBLE : View.GONE);
        watchlist.setVisibility(inWatchlist ? View.VISIBLE : View.GONE);

        String airTimeString = null;
        if (airDay != null && airTime != null) {
            airTimeString = airDay + " " + airTime;
        }
        if (network != null) {
            if (airTimeString != null) {
                airTimeString += ", " + network;
            } else {
                airTimeString = network;
            }
        }

        this.airTime.setText(airTimeString);
        this.certification.setText(certification);
        this.overview.setText(overview);

        setContentVisible(true);
        invalidateMenu();
    }

    private void updateGenreViews(final Cursor cursor) {
        if (cursor.getCount() > 0) {
            StringBuilder sb = new StringBuilder();
            final int genreColumnIndex = cursor.getColumnIndex(ShowGenreColumns.GENRE);

            cursor.moveToPosition(-1);

            while (cursor.moveToNext()) {
                sb.append(cursor.getString(genreColumnIndex));
                if (!cursor.isLast())
                    sb.append(", ");
            }

            genres = sb.toString();
        } else {
            genres = null;
        }

        updateTitle();
    }

    private void updateActorViews(Cursor c) {
        actors.removeAllViews();
        final int count = c.getCount();
        Timber.d("Actor count: %d", count);
        final int visibility = count > 0 ? View.VISIBLE : View.GONE;
        actorsTitle.setVisibility(visibility);
        actorsParent.setVisibility(visibility);

        c.moveToPosition(-1);
        while (c.moveToNext()) {
            View v = LayoutInflater.from(getActivity()).inflate(R.layout.person, actors, false);

            RemoteImageView headshot = (RemoteImageView) v.findViewById(R.id.headshot);
            headshot.setImage(c.getString(c.getColumnIndex(PersonColumns.HEADSHOT)));
            TextView name = (TextView) v.findViewById(R.id.name);
            name.setText(c.getString(c.getColumnIndex(PersonColumns.NAME)));
            TextView character = (TextView) v.findViewById(R.id.job);
            character.setText(c.getString(c.getColumnIndex(ShowCharacterColumns.CHARACTER)));

            actors.addView(v);
        }
    }

    private void updateEpisodeWatchViews(Cursor cursor) {
        if (cursor.moveToFirst()) {
            toWatch.setVisibility(View.VISIBLE);
            watchTitle.setVisibility(View.VISIBLE);

            toWatchId = cursor.getLong(cursor.getColumnIndex(ShowColumns.ID));
            toWatchTitle = cursor.getString(cursor.getColumnIndex(EpisodeColumns.TITLE));

            toWatchHolder.episodeTitle.setText(toWatchTitle);

            final long airTime = cursor.getLong(cursor.getColumnIndex(EpisodeColumns.FIRST_AIRED));

            final int season = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.SEASON));
            final int episode = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.EPISODE));
            toWatchHolder.episodeEpisode.setText("S" + season + "E" + episode);

            final String screenshotUrl = cursor.getString(cursor.getColumnIndex(EpisodeColumns.SCREENSHOT));
            toWatchHolder.episodeScreenshot.setImage(screenshotUrl);

            String airTimeStr = DateUtils.millisToString(getActivity(), airTime, false);

            final boolean watching = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.WATCHING)) == 1;
            final boolean checkedIn = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.CHECKED_IN)) == 1;

            toWatchHolder.episodeOverflow.removeItems();
            if (checkedIn) {
                toWatchHolder.episodeOverflow.addItem(R.id.action_checkin_cancel, R.string.action_checkin_cancel);
                airTimeStr = getResources().getString(R.string.show_watching);
            } else if (!watching) {
                toWatchHolder.episodeOverflow.addItem(R.id.action_checkin, R.string.action_checkin);
                toWatchHolder.episodeOverflow.addItem(R.id.action_watched, R.string.action_watched);
            }

            toWatchHolder.episodeAirTime.setText(airTimeStr);
        } else {
            toWatch.setVisibility(View.GONE);
            watchTitle.setVisibility(View.GONE);
            toWatchId = -1;
        }

        if (lastWatched != null) {
            if (cursor.moveToNext()) {
                lastWatched.setVisibility(View.VISIBLE);

                lastWatchedId = cursor.getLong(cursor.getColumnIndex(ShowColumns.ID));

                lastWatchedHolder.episodeTitle
                        .setText(cursor.getString(cursor.getColumnIndex(EpisodeColumns.TITLE)));

                final long airTime = cursor.getLong(cursor.getColumnIndex(EpisodeColumns.FIRST_AIRED));
                final String airTimeStr = DateUtils.millisToString(getActivity(), airTime, false);
                lastWatchedHolder.episodeAirTime.setText(airTimeStr);

                final int season = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.SEASON));
                final int episode = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.EPISODE));
                lastWatchedHolder.episodeEpisode.setText("S" + season + "E" + episode);

                final String screenshotUrl = cursor.getString(cursor.getColumnIndex(EpisodeColumns.SCREENSHOT));
                lastWatchedHolder.episodeScreenshot.setImage(screenshotUrl);
            } else {
                lastWatched.setVisibility(toWatchId == -1 ? View.GONE : View.INVISIBLE);
                lastWatchedId = -1;
            }
        }

        if (toWatchId == -1 && lastWatchedId == -1 && toCollectId == -1 && lastCollectedId == -1) {
            episodes.setVisibility(View.GONE);
        } else {
            episodes.setVisibility(View.VISIBLE);
        }
    }

    private void updateEpisodeCollectViews(Cursor cursor) {
        if (cursor.moveToFirst()) {
            toCollect.setVisibility(View.VISIBLE);
            collectTitle.setVisibility(View.VISIBLE);

            toCollectId = cursor.getLong(cursor.getColumnIndex(ShowColumns.ID));

            toCollectHolder.episodeTitle.setText(cursor.getString(cursor.getColumnIndex(EpisodeColumns.TITLE)));

            final long airTime = cursor.getLong(cursor.getColumnIndex(EpisodeColumns.FIRST_AIRED));
            final String airTimeStr = DateUtils.millisToString(getActivity(), airTime, false);
            toCollectHolder.episodeAirTime.setText(airTimeStr);

            final int season = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.SEASON));
            final int episode = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.EPISODE));
            toCollectHolder.episodeEpisode.setText("S" + season + "E" + episode);

            final String screenshotUrl = cursor.getString(cursor.getColumnIndex(EpisodeColumns.SCREENSHOT));
            toCollectHolder.episodeScreenshot.setImage(screenshotUrl);
        } else {
            toCollect.setVisibility(View.GONE);
            collectTitle.setVisibility(View.GONE);
            toCollectId = -1;
        }

        if (lastCollected != null) {
            if (cursor.moveToNext()) {
                lastCollected.setVisibility(View.VISIBLE);

                lastCollectedId = cursor.getLong(cursor.getColumnIndex(ShowColumns.ID));

                lastCollectedHolder.episodeTitle
                        .setText(cursor.getString(cursor.getColumnIndex(EpisodeColumns.TITLE)));

                final long airTime = cursor.getLong(cursor.getColumnIndex(EpisodeColumns.FIRST_AIRED));
                final String airTimeStr = DateUtils.millisToString(getActivity(), airTime, false);
                lastCollectedHolder.episodeAirTime.setText(airTimeStr);

                final int season = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.SEASON));
                final int episode = cursor.getInt(cursor.getColumnIndex(EpisodeColumns.EPISODE));
                lastCollectedHolder.episodeEpisode.setText("S" + season + "E" + episode);

                // TODO: Fanart? Is SCREEN missing?
                final String screenshotUrl = cursor.getString(cursor.getColumnIndex(EpisodeColumns.SCREENSHOT));
                lastCollectedHolder.episodeScreenshot.setImage(screenshotUrl);
            } else {
                lastCollectedId = -1;
                lastCollected.setVisibility(View.INVISIBLE);
            }
        }

        if (toWatchId == -1 && lastWatchedId == -1 && toCollectId == -1 && lastCollectedId == -1) {
            episodes.setVisibility(View.GONE);
        } else {
            episodes.setVisibility(View.VISIBLE);
        }
    }

    private LoaderManager.LoaderCallbacks<SimpleCursor> showCallbacks = new LoaderManager.LoaderCallbacks<SimpleCursor>() {
        @Override
        public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) {
            SimpleCursorLoader cl = new SimpleCursorLoader(getActivity(), Shows.withId(showId), SHOW_PROJECTION,
                    null, null, null);
            cl.setUpdateThrottle(2 * android.text.format.DateUtils.SECOND_IN_MILLIS);
            return cl;
        }

        @Override
        public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor data) {
            updateShowView(data);
        }

        @Override
        public void onLoaderReset(Loader<SimpleCursor> cursorLoader) {
        }
    };

    private LoaderManager.LoaderCallbacks<SimpleCursor> genreCallbacks = new LoaderManager.LoaderCallbacks<SimpleCursor>() {
        @Override
        public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) {
            SimpleCursorLoader cl = new SimpleCursorLoader(getActivity(), ShowGenres.fromShow(showId),
                    GENRES_PROJECTION, null, null, ShowGenres.DEFAULT_SORT);
            cl.setUpdateThrottle(2 * android.text.format.DateUtils.SECOND_IN_MILLIS);
            return cl;
        }

        @Override
        public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor data) {
            updateGenreViews(data);
        }

        @Override
        public void onLoaderReset(Loader<SimpleCursor> cursorLoader) {
        }
    };

    private static final String[] CHARACTERS_PROJECTION = new String[] {
            Tables.SHOW_CHARACTERS + "." + ShowCharacterColumns.ID,
            Tables.SHOW_CHARACTERS + "." + ShowCharacterColumns.CHARACTER, Tables.PEOPLE + "." + PersonColumns.NAME,
            Tables.PEOPLE + "." + PersonColumns.HEADSHOT, };

    private LoaderManager.LoaderCallbacks<SimpleCursor> charactersCallback = new LoaderManager.LoaderCallbacks<SimpleCursor>() {
        @Override
        public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) {
            SimpleCursorLoader cl = new SimpleCursorLoader(getActivity(),
                    ProviderSchematic.ShowCharacters.fromShow(showId), CHARACTERS_PROJECTION,
                    Tables.PEOPLE + "." + PersonColumns.NEEDS_SYNC + "=0", null, null);
            cl.setUpdateThrottle(2 * android.text.format.DateUtils.SECOND_IN_MILLIS);
            return cl;
        }

        @Override
        public void onLoadFinished(Loader<SimpleCursor> loader, SimpleCursor data) {
            updateActorViews(data);
        }

        @Override
        public void onLoaderReset(Loader<SimpleCursor> loader) {
        }
    };

    private LoaderManager.LoaderCallbacks<SimpleMergeCursor> episodeWatchCallbacks = new LoaderManager.LoaderCallbacks<SimpleMergeCursor>() {
        @Override
        public Loader<SimpleMergeCursor> onCreateLoader(int i, Bundle bundle) {
            return new WatchedLoader(getActivity(), showId, EPISODE_PROJECTION);
        }

        @Override
        public void onLoadFinished(Loader<SimpleMergeCursor> cursorLoader, SimpleMergeCursor cursor) {
            updateEpisodeWatchViews(cursor);
        }

        @Override
        public void onLoaderReset(Loader<SimpleMergeCursor> cursorLoader) {
        }
    };

    private LoaderManager.LoaderCallbacks<SimpleMergeCursor> episodeCollectCallbacks = new LoaderManager.LoaderCallbacks<SimpleMergeCursor>() {
        @Override
        public Loader<SimpleMergeCursor> onCreateLoader(int i, Bundle bundle) {
            return new CollectLoader(getActivity(), showId, EPISODE_PROJECTION);
        }

        @Override
        public void onLoadFinished(Loader<SimpleMergeCursor> cursorLoader, SimpleMergeCursor cursor) {
            updateEpisodeCollectViews(cursor);
        }

        @Override
        public void onLoaderReset(Loader<SimpleMergeCursor> cursorLoader) {
        }
    };

    private LoaderManager.LoaderCallbacks<SimpleCursor> seasonsLoader = new LoaderManager.LoaderCallbacks<SimpleCursor>() {
        @Override
        public Loader<SimpleCursor> onCreateLoader(int id, Bundle args) {
            SimpleCursorLoader cl = new SimpleCursorLoader(getActivity(), Seasons.fromShow(showId),
                    SeasonsAdapter.PROJECTION, null, null, Seasons.DEFAULT_SORT);
            cl.setUpdateThrottle(2 * android.text.format.DateUtils.SECOND_IN_MILLIS);
            return cl;
        }

        @Override
        public void onLoadFinished(Loader<SimpleCursor> cursorLoader, SimpleCursor data) {
            seasonsCursor = data;
            seasonsAdapter.changeCursor(data);
        }

        @Override
        public void onLoaderReset(Loader<SimpleCursor> cursorLoader) {
            seasonsCursor = null;
            seasonsAdapter.changeCursor(null);
        }
    };
}