Redesign tv shows details, to not use tabs or expandable lists in episodes list
First version, to be improved visually
This commit is contained in:
parent
d993126c7e
commit
43371b9480
|
@ -200,6 +200,23 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
|
||||||
*/
|
*/
|
||||||
abstract protected String getListSyncType();
|
abstract protected String getListSyncType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the {@link LibrarySyncService} syncID if this fragment
|
||||||
|
* synchronizes a single item. The itemId that should be synced must returned by {@link #getSyncItemID()}
|
||||||
|
* @return {@link LibrarySyncService} SyncID if syncing a single item. Null if not aplicable
|
||||||
|
*/
|
||||||
|
protected String getSyncID() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the item ID for SyncID returned by {@link #getSyncID()}
|
||||||
|
* @return -1 if not used.
|
||||||
|
*/
|
||||||
|
protected int getSyncItemID() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event bus post. Called when the syncing process ended
|
* Event bus post. Called when the syncing process ended
|
||||||
*
|
*
|
||||||
|
@ -238,10 +255,8 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
public void onServiceConnected(LibrarySyncService librarySyncService) {
|
||||||
if(SyncUtils.isLibrarySyncing(
|
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||||
librarySyncService,
|
if(SyncUtils.isLibrarySyncing(librarySyncService, hostInfo, getListSyncType())) {
|
||||||
HostManager.getInstance(getActivity()).getHostInfo(),
|
|
||||||
getListSyncType())) {
|
|
||||||
showRefreshAnimation();
|
showRefreshAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +278,13 @@ public abstract class AbstractCursorListFragment extends AbstractListFragment
|
||||||
// Start the syncing process
|
// Start the syncing process
|
||||||
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
Intent syncIntent = new Intent(this.getActivity(), LibrarySyncService.class);
|
||||||
syncIntent.putExtra(getListSyncType(), true);
|
syncIntent.putExtra(getListSyncType(), true);
|
||||||
|
|
||||||
|
String syncID = getSyncID();
|
||||||
|
int itemId = getSyncItemID();
|
||||||
|
if ((syncID != null) && (itemId != -1)) {
|
||||||
|
syncIntent.putExtra(syncID, itemId);
|
||||||
|
}
|
||||||
|
|
||||||
getActivity().startService(syncIntent);
|
getActivity().startService(syncIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,11 +208,11 @@ public class MoviesActivity extends BaseActivity
|
||||||
selectedMovieTitle = vh.movieTitle;
|
selectedMovieTitle = vh.movieTitle;
|
||||||
selectedMovieId = vh.movieId;
|
selectedMovieId = vh.movieId;
|
||||||
|
|
||||||
|
final MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh);
|
||||||
|
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
// Set up transitions
|
// Set up transitions
|
||||||
if (Utils.isLollipopOrLater()) {
|
if (Utils.isLollipopOrLater()) {
|
||||||
final MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh);
|
|
||||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
|
||||||
|
|
||||||
android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() {
|
android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
|
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
|
||||||
|
@ -230,31 +230,24 @@ public class MoviesActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
movieDetailsFragment.setEnterSharedElementCallback(seCallback);
|
movieDetailsFragment.setEnterSharedElementCallback(seCallback);
|
||||||
|
movieDetailsFragment.setEnterTransition(
|
||||||
movieDetailsFragment.setEnterTransition(TransitionInflater
|
TransitionInflater.from(this)
|
||||||
.from(this)
|
.inflateTransition(R.transition.media_details));
|
||||||
.inflateTransition(R.transition.media_details));
|
|
||||||
movieDetailsFragment.setReturnTransition(null);
|
movieDetailsFragment.setReturnTransition(null);
|
||||||
|
|
||||||
Transition changeImageTransition = TransitionInflater.from(
|
Transition changeImageTransition =
|
||||||
this).inflateTransition(R.transition.change_image);
|
TransitionInflater.from(this).inflateTransition(R.transition.change_image);
|
||||||
movieDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
|
movieDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
|
||||||
movieDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
|
movieDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
|
||||||
|
|
||||||
fragTrans.replace(R.id.fragment_container, movieDetailsFragment)
|
fragTrans.addSharedElement(vh.artView, vh.artView.getTransitionName());
|
||||||
.addToBackStack(null)
|
|
||||||
.addSharedElement(vh.artView, vh.artView.getTransitionName())
|
|
||||||
.commit();
|
|
||||||
} else {
|
} else {
|
||||||
MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh);
|
|
||||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
|
||||||
|
|
||||||
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
|
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
|
||||||
R.anim.fragment_list_popenter, 0);
|
R.anim.fragment_list_popenter, 0);
|
||||||
fragTrans.replace(R.id.fragment_container, movieDetailsFragment)
|
|
||||||
.addToBackStack(null)
|
|
||||||
.commit();
|
|
||||||
}
|
}
|
||||||
|
fragTrans.replace(R.id.fragment_container, movieDetailsFragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
|
||||||
setupActionBar(selectedMovieTitle);
|
setupActionBar(selectedMovieTitle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,49 @@
|
||||||
package org.xbmc.kore.ui;
|
package org.xbmc.kore.ui;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.provider.BaseColumns;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.app.LoaderManager;
|
||||||
|
import android.support.v4.content.CursorLoader;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import com.astuetz.PagerSlidingTabStrip;
|
import android.widget.GridLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
|
import org.xbmc.kore.Settings;
|
||||||
|
import org.xbmc.kore.host.HostManager;
|
||||||
|
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
||||||
|
import org.xbmc.kore.jsonrpc.type.VideoType;
|
||||||
|
import org.xbmc.kore.provider.MediaContract;
|
||||||
|
import org.xbmc.kore.service.library.LibrarySyncService;
|
||||||
import org.xbmc.kore.utils.LogUtils;
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
import org.xbmc.kore.utils.TabsAdapter;
|
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
import org.xbmc.kore.utils.UIUtils;
|
||||||
import org.xbmc.kore.utils.Utils;
|
import org.xbmc.kore.utils.Utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.InjectView;
|
import butterknife.InjectView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for the TV Show overview and Episodes list
|
* Presents a TV Show overview
|
||||||
*/
|
*/
|
||||||
public class TVShowDetailsFragment extends Fragment {
|
public class TVShowDetailsFragment extends AbstractDetailsFragment
|
||||||
|
implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
private static final String TAG = LogUtils.makeLogTag(TVShowDetailsFragment.class);
|
private static final String TAG = LogUtils.makeLogTag(TVShowDetailsFragment.class);
|
||||||
|
|
||||||
public static final String POSTER_TRANS_NAME = "POSTER_TRANS_NAME";
|
public static final String POSTER_TRANS_NAME = "POSTER_TRANS_NAME";
|
||||||
|
@ -51,13 +72,54 @@ public class TVShowDetailsFragment extends Fragment {
|
||||||
public static final String BUNDLE_KEY_PLOT = "plot";
|
public static final String BUNDLE_KEY_PLOT = "plot";
|
||||||
public static final String BUNDLE_KEY_GENRES = "genres";
|
public static final String BUNDLE_KEY_GENRES = "genres";
|
||||||
|
|
||||||
|
public interface OnSeasonSelectedListener {
|
||||||
|
void onSeasonSelected(int tvshowId, int season);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity listener
|
||||||
|
private OnSeasonSelectedListener listenerActivity;
|
||||||
|
|
||||||
|
// Loader IDs
|
||||||
|
private static final int LOADER_TVSHOW = 0,
|
||||||
|
LOADER_SEASONS = 1,
|
||||||
|
LOADER_CAST = 2;
|
||||||
|
|
||||||
// Displayed movie id
|
// Displayed movie id
|
||||||
private int tvshowId = -1;
|
private int tvshowId = -1;
|
||||||
|
private String tvshowTitle;
|
||||||
|
|
||||||
private TabsAdapter tabsAdapter;
|
// Controls whether a automatic sync refresh has been issued for this show
|
||||||
|
private static boolean hasIssuedOutdatedRefresh = false;
|
||||||
|
|
||||||
@InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip;
|
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
||||||
@InjectView(R.id.pager) ViewPager viewPager;
|
|
||||||
|
// // Buttons
|
||||||
|
// @InjectView(R.id.go_to_imdb) ImageButton imdbButton;
|
||||||
|
|
||||||
|
// Detail views
|
||||||
|
@InjectView(R.id.media_panel) ScrollView mediaPanel;
|
||||||
|
|
||||||
|
@InjectView(R.id.art) ImageView mediaArt;
|
||||||
|
@InjectView(R.id.poster) ImageView mediaPoster;
|
||||||
|
|
||||||
|
@InjectView(R.id.media_title) TextView mediaTitle;
|
||||||
|
@InjectView(R.id.media_undertitle) TextView mediaUndertitle;
|
||||||
|
|
||||||
|
@InjectView(R.id.rating) TextView mediaRating;
|
||||||
|
@InjectView(R.id.max_rating) TextView mediaMaxRating;
|
||||||
|
@InjectView(R.id.premiered) TextView mediaPremiered;
|
||||||
|
@InjectView(R.id.genres) TextView mediaGenres;
|
||||||
|
|
||||||
|
@InjectView(R.id.media_description) TextView mediaDescription;
|
||||||
|
@InjectView(R.id.cast_list) GridLayout videoCastList;
|
||||||
|
|
||||||
|
@InjectView(R.id.seasons_title) TextView seasonsListTitle;
|
||||||
|
@InjectView(R.id.seasons_list) GridLayout seasonsList;
|
||||||
|
|
||||||
|
@InjectView(R.id.media_description_container) LinearLayout mediaDescriptionContainer;
|
||||||
|
@InjectView(R.id.show_all) ImageView mediaShowAll;
|
||||||
|
|
||||||
|
private boolean isDescriptionExpanded = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of this, initialized to show tvshowId
|
* Create a new instance of this, initialized to show tvshowId
|
||||||
|
@ -84,61 +146,452 @@ public class TVShowDetailsFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
@TargetApi(21)
|
||||||
super.onCreate(savedInstanceState);
|
protected View createView(LayoutInflater inflater, ViewGroup container) {
|
||||||
}
|
Bundle bundle = getArguments();
|
||||||
|
tvshowId = bundle.getInt(BUNDLE_KEY_TVSHOWID, -1);
|
||||||
|
|
||||||
@Override
|
if (tvshowId == -1) {
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
// There's nothing to show
|
||||||
tvshowId = getArguments().getInt(BUNDLE_KEY_TVSHOWID, -1);
|
|
||||||
|
|
||||||
if ((container == null) || (tvshowId == -1)) {
|
|
||||||
// We're not being shown or there's nothing to show
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_default_view_pager, container, false);
|
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_tvshow_overview, container, false);
|
||||||
ButterKnife.inject(this, root);
|
ButterKnife.inject(this, root);
|
||||||
|
|
||||||
long baseFragmentId = tvshowId * 10;
|
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
||||||
tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager())
|
|
||||||
.addTab(TVShowOverviewFragment.class, getArguments(), R.string.tvshow_overview,
|
|
||||||
baseFragmentId)
|
|
||||||
.addTab(TVShowEpisodeListFragment.class, getArguments(),
|
|
||||||
R.string.tvshow_episodes, baseFragmentId + 1);
|
|
||||||
|
|
||||||
viewPager.setAdapter(tabsAdapter);
|
// Setup dim the fanart when scroll changes. Full dim on 4 * iconSize dp
|
||||||
pagerTabStrip.setViewPager(viewPager);
|
Resources resources = getActivity().getResources();
|
||||||
|
final int pixelsToTransparent = 4 * resources.getDimensionPixelSize(R.dimen.default_icon_size);
|
||||||
|
mediaPanel.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrollChanged() {
|
||||||
|
float y = mediaPanel.getScrollY();
|
||||||
|
float newAlpha = Math.min(1, Math.max(0, 1 - (y / pixelsToTransparent)));
|
||||||
|
mediaArt.setAlpha(newAlpha);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tvshowTitle = bundle.getString(BUNDLE_KEY_TITLE);
|
||||||
|
|
||||||
|
mediaTitle.setText(tvshowTitle);
|
||||||
|
setMediaUndertitle(bundle.getInt(BUNDLE_KEY_EPISODE), bundle.getInt(BUNDLE_KEY_WATCHEDEPISODES));
|
||||||
|
setMediaPremiered(bundle.getString(BUNDLE_KEY_PREMIERED), bundle.getString(BUNDLE_KEY_STUDIO));
|
||||||
|
mediaGenres.setText(bundle.getString(BUNDLE_KEY_GENRES));
|
||||||
|
setMediaRating(bundle.getDouble(BUNDLE_KEY_RATING));
|
||||||
|
mediaDescription.setText(bundle.getString(BUNDLE_KEY_PLOT));
|
||||||
|
|
||||||
|
if(Utils.isLollipopOrLater()) {
|
||||||
|
mediaPoster.setTransitionName(getArguments().getString(POSTER_TRANS_NAME));
|
||||||
|
}
|
||||||
|
// Pad main content view to overlap with bottom system bar
|
||||||
|
// UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true);
|
||||||
|
// mediaPanel.setClipToPadding(false);
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSyncType() {
|
||||||
|
return LibrarySyncService.SYNC_SINGLE_TVSHOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSyncID() {
|
||||||
|
return LibrarySyncService.SYNC_TVSHOWID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSyncItemID() {
|
||||||
|
return tvshowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SwipeRefreshLayout getSwipeRefreshLayout() {
|
||||||
|
return swipeRefreshLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDownload() { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated (Bundle savedInstanceState) {
|
public void onActivityCreated (Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
setHasOptionsMenu(false);
|
|
||||||
|
hasIssuedOutdatedRefresh = false;
|
||||||
|
|
||||||
|
// Start the loaders
|
||||||
|
getLoaderManager().initLoader(LOADER_TVSHOW, null, this);
|
||||||
|
getLoaderManager().initLoader(LOADER_SEASONS, null, this);
|
||||||
|
getLoaderManager().initLoader(LOADER_CAST, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Fragment getCurrentTabFragment() {
|
@Override
|
||||||
return tabsAdapter.getItem(viewPager.getCurrentItem());
|
public void onAttach(Context activity) {
|
||||||
|
super.onAttach(activity);
|
||||||
|
try {
|
||||||
|
listenerActivity = (OnSeasonSelectedListener) activity;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(activity.toString() + " must implement OnSeasonSelectedListener");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
listenerActivity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||||
|
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||||
|
getLoaderManager().restartLoader(LOADER_TVSHOW, null, this);
|
||||||
|
getLoaderManager().restartLoader(LOADER_SEASONS, null, this);
|
||||||
|
getLoaderManager().restartLoader(LOADER_CAST, null, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loader callbacks
|
||||||
|
*/
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||||
|
Uri uri;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case LOADER_TVSHOW:
|
||||||
|
uri = MediaContract.TVShows.buildTVShowUri(getHostInfo().getId(), tvshowId);
|
||||||
|
return new CursorLoader(getActivity(), uri,
|
||||||
|
TVShowDetailsQuery.PROJECTION, null, null, null);
|
||||||
|
case LOADER_SEASONS:
|
||||||
|
// Load seasons
|
||||||
|
uri = MediaContract.Seasons.buildTVShowSeasonsListUri(getHostInfo().getId(), tvshowId);
|
||||||
|
return new CursorLoader(getActivity(), uri,
|
||||||
|
SeasonsListQuery.PROJECTION, null, null, SeasonsListQuery.SORT);
|
||||||
|
case LOADER_CAST:
|
||||||
|
uri = MediaContract.TVShowCast.buildTVShowCastListUri(getHostInfo().getId(), tvshowId);
|
||||||
|
return new CursorLoader(getActivity(), uri,
|
||||||
|
TVShowCastListQuery.PROJECTION, null, null, TVShowCastListQuery.SORT);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
||||||
|
LogUtils.LOGD(TAG, "onLoadFinished");
|
||||||
|
if (cursor != null && cursor.getCount() > 0) {
|
||||||
|
switch (cursorLoader.getId()) {
|
||||||
|
case LOADER_TVSHOW:
|
||||||
|
displayTVShowDetails(cursor);
|
||||||
|
checkOutdatedTVShowDetails(cursor);
|
||||||
|
break;
|
||||||
|
case LOADER_SEASONS:
|
||||||
|
displaySeasonList(cursor);
|
||||||
|
break;
|
||||||
|
case LOADER_CAST:
|
||||||
|
displayCastList(cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
||||||
|
// Release loader's data
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Callbacks for button bar
|
||||||
|
// */
|
||||||
|
// @OnClick(R.id.go_to_imdb)
|
||||||
|
// public void onImdbClicked(View v) {
|
||||||
|
// String imdbNumber = (String)v.getTag();
|
||||||
|
//
|
||||||
|
// if (imdbNumber != null) {
|
||||||
|
// Utils.openImdbForMovie(getActivity(), imdbNumber);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the tv show details
|
||||||
|
*
|
||||||
|
* @param cursor Cursor with the data
|
||||||
|
*/
|
||||||
|
private void displayTVShowDetails(Cursor cursor) {
|
||||||
|
LogUtils.LOGD(TAG, "displayTVShowDetails");
|
||||||
|
cursor.moveToFirst();
|
||||||
|
tvshowTitle = cursor.getString(TVShowDetailsQuery.TITLE);
|
||||||
|
mediaTitle.setText(tvshowTitle);
|
||||||
|
int numEpisodes = cursor.getInt(TVShowDetailsQuery.EPISODE),
|
||||||
|
watchedEpisodes = cursor.getInt(TVShowDetailsQuery.WATCHEDEPISODES);
|
||||||
|
setMediaUndertitle(numEpisodes, watchedEpisodes);
|
||||||
|
|
||||||
|
setMediaPremiered(cursor.getString(TVShowDetailsQuery.PREMIERED), cursor.getString(TVShowDetailsQuery.STUDIO));
|
||||||
|
|
||||||
|
mediaGenres.setText(cursor.getString(TVShowDetailsQuery.GENRES));
|
||||||
|
|
||||||
|
setMediaRating(cursor.getDouble(TVShowDetailsQuery.RATING));
|
||||||
|
|
||||||
|
mediaDescription.setText(cursor.getString(TVShowDetailsQuery.PLOT));
|
||||||
|
|
||||||
|
Resources resources = getActivity().getResources();
|
||||||
|
final int maxLines = resources.getInteger(R.integer.description_max_lines);
|
||||||
|
TypedArray styledAttributes = getActivity().getTheme().obtainStyledAttributes(new int[] {
|
||||||
|
R.attr.iconExpand,
|
||||||
|
R.attr.iconCollapse
|
||||||
|
});
|
||||||
|
final int iconCollapseResId =
|
||||||
|
styledAttributes.getResourceId(styledAttributes.getIndex(0), R.drawable.ic_expand_less_white_24dp);
|
||||||
|
final int iconExpandResId =
|
||||||
|
styledAttributes.getResourceId(styledAttributes.getIndex(1), R.drawable.ic_expand_more_white_24dp);
|
||||||
|
styledAttributes.recycle();
|
||||||
|
mediaDescriptionContainer.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (!isDescriptionExpanded) {
|
||||||
|
mediaDescription.setMaxLines(Integer.MAX_VALUE);
|
||||||
|
mediaShowAll.setImageResource(iconExpandResId);
|
||||||
|
} else {
|
||||||
|
mediaDescription.setMaxLines(maxLines);
|
||||||
|
mediaShowAll.setImageResource(iconCollapseResId);
|
||||||
|
}
|
||||||
|
isDescriptionExpanded = !isDescriptionExpanded;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// // IMDB button
|
||||||
|
// imdbButton.setTag(cursor.getString(TVShowDetailsQuery.IMDBNUMBER));
|
||||||
|
|
||||||
|
// Images
|
||||||
|
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||||
|
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
|
|
||||||
|
int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
||||||
|
int posterHeight = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height);
|
||||||
|
UIUtils.loadImageWithCharacterAvatar(getActivity(), getHostManager(),
|
||||||
|
cursor.getString(TVShowDetailsQuery.THUMBNAIL), tvshowTitle,
|
||||||
|
mediaPoster, posterWidth, posterHeight);
|
||||||
|
int artHeight = resources.getDimensionPixelOffset(R.dimen.now_playing_art_height);
|
||||||
|
UIUtils.loadImageIntoImageview(getHostManager(),
|
||||||
|
cursor.getString(TVShowDetailsQuery.FANART),
|
||||||
|
mediaArt, displayMetrics.widthPixels, artHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMediaUndertitle(int numEpisodes, int watchedEpisodes) {
|
||||||
|
String episodes = String.format(getString(R.string.num_episodes),
|
||||||
|
numEpisodes, numEpisodes - watchedEpisodes);
|
||||||
|
mediaUndertitle.setText(episodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMediaPremiered(String premiered, String studio) {
|
||||||
|
mediaPremiered.setText(String.format(getString(R.string.premiered), premiered) + " | " + studio);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMediaRating(double rating) {
|
||||||
|
if (rating > 0) {
|
||||||
|
mediaRating.setVisibility(View.VISIBLE);
|
||||||
|
mediaMaxRating.setVisibility(View.VISIBLE);
|
||||||
|
mediaRating.setText(String.format("%01.01f", rating));
|
||||||
|
mediaMaxRating.setText(getString(R.string.max_rating_video));
|
||||||
|
} else {
|
||||||
|
mediaRating.setVisibility(View.INVISIBLE);
|
||||||
|
mediaMaxRating.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the cast details
|
||||||
|
*
|
||||||
|
* @param cursor Cursor with the data
|
||||||
|
*/
|
||||||
|
private void displayCastList(Cursor cursor) {
|
||||||
|
// Transform the cursor into a List<VideoType.Cast>
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
ArrayList<VideoType.Cast> castArrayList = new ArrayList<VideoType.Cast>(cursor.getCount());
|
||||||
|
do {
|
||||||
|
castArrayList.add(new VideoType.Cast(cursor.getString(TVShowCastListQuery.NAME),
|
||||||
|
cursor.getInt(TVShowCastListQuery.ORDER),
|
||||||
|
cursor.getString(TVShowCastListQuery.ROLE),
|
||||||
|
cursor.getString(TVShowCastListQuery.THUMBNAIL)));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
|
||||||
|
UIUtils.setupCastInfo(getActivity(), castArrayList, videoCastList,
|
||||||
|
AllCastActivity.buildLaunchIntent(getActivity(), tvshowTitle, castArrayList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the seasons list
|
||||||
|
*
|
||||||
|
* @param cursor Cursor with the data
|
||||||
|
*/
|
||||||
|
private void displaySeasonList(Cursor cursor) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
HostManager hostManager = HostManager.getInstance(getActivity());
|
||||||
|
|
||||||
|
View.OnClickListener seasonListClickListener = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
listenerActivity.onSeasonSelected(tvshowId, (int)v.getTag());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the art dimensions
|
||||||
|
Resources resources = getActivity().getResources();
|
||||||
|
int artWidth = (int)(resources.getDimension(R.dimen.seasonlist_art_width) /
|
||||||
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||||
|
int artHeight = (int)(resources.getDimension(R.dimen.seasonlist_art_heigth) /
|
||||||
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||||
|
|
||||||
|
seasonsList.removeAllViews();
|
||||||
|
do {
|
||||||
|
int seasonNumber = cursor.getInt(SeasonsListQuery.SEASON);
|
||||||
|
String thumbnail = cursor.getString(SeasonsListQuery.THUMBNAIL);
|
||||||
|
int numEpisodes = cursor.getInt(SeasonsListQuery.EPISODE);
|
||||||
|
int watchedEpisodes = cursor.getInt(SeasonsListQuery.WATCHEDEPISODES);
|
||||||
|
|
||||||
|
View seasonView = LayoutInflater.from(getActivity()).inflate(R.layout.grid_item_season, seasonsList, false);
|
||||||
|
|
||||||
|
ImageView seasonPictureView = (ImageView) seasonView.findViewById(R.id.art);
|
||||||
|
TextView seasonNumberView = (TextView) seasonView.findViewById(R.id.season);
|
||||||
|
TextView seasonEpisodesView = (TextView) seasonView.findViewById(R.id.episodes);
|
||||||
|
|
||||||
|
seasonNumberView.setText(String.format(getActivity().getString(R.string.season_number), seasonNumber));
|
||||||
|
seasonEpisodesView.setText(String.format(getActivity().getString(R.string.num_episodes),
|
||||||
|
numEpisodes, numEpisodes - watchedEpisodes));
|
||||||
|
|
||||||
|
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
|
||||||
|
thumbnail,
|
||||||
|
String.valueOf(seasonNumber),
|
||||||
|
seasonPictureView, artWidth, artHeight);
|
||||||
|
|
||||||
|
seasonView.setTag(seasonNumber);
|
||||||
|
seasonView.setOnClickListener(seasonListClickListener);
|
||||||
|
seasonsList.addView(seasonView);
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
} else {
|
||||||
|
// No seasons, hide views
|
||||||
|
seasonsListTitle.setVisibility(View.GONE);
|
||||||
|
seasonsList.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wether we should refresh the TV Show details with the info on XBMC
|
||||||
|
* The details will be updated if the last update is older than what is configured in the
|
||||||
|
* settings
|
||||||
|
*
|
||||||
|
* @param cursor Cursor with the data
|
||||||
|
*/
|
||||||
|
private void checkOutdatedTVShowDetails(Cursor cursor) {
|
||||||
|
if (hasIssuedOutdatedRefresh)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cursor.moveToFirst();
|
||||||
|
long lastUpdated = cursor.getLong(TVShowDetailsQuery.UPDATED);
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() > lastUpdated + Settings.DB_UPDATE_INTERVAL) {
|
||||||
|
// Trigger a silent refresh
|
||||||
|
hasIssuedOutdatedRefresh = true;
|
||||||
|
startSync(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the shared element if visible
|
||||||
|
* @return View if visible, null otherwise
|
||||||
|
*/
|
||||||
public View getSharedElement() {
|
public View getSharedElement() {
|
||||||
View view = getView();
|
if (UIUtils.isViewInBounds(mediaPanel, mediaPoster)) {
|
||||||
if (view == null)
|
return mediaPoster;
|
||||||
return null;
|
|
||||||
|
|
||||||
//Note: this works as R.id.poster is only used in TVShowOverviewFragment.
|
|
||||||
//If the same id is used in other fragments in the TabsAdapter we
|
|
||||||
//need to check which fragment is currently displayed
|
|
||||||
View artView = view.findViewById(R.id.poster);
|
|
||||||
View scrollView = view.findViewById(R.id.media_panel);
|
|
||||||
if (( artView != null ) &&
|
|
||||||
( scrollView != null ) &&
|
|
||||||
UIUtils.isViewInBounds(scrollView, artView)) {
|
|
||||||
return artView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV Show details query parameters.
|
||||||
|
*/
|
||||||
|
private interface TVShowDetailsQuery {
|
||||||
|
String[] PROJECTION = {
|
||||||
|
BaseColumns._ID,
|
||||||
|
MediaContract.TVShows.TITLE,
|
||||||
|
MediaContract.TVShows.THUMBNAIL,
|
||||||
|
MediaContract.TVShows.FANART,
|
||||||
|
MediaContract.TVShows.PREMIERED,
|
||||||
|
MediaContract.TVShows.STUDIO,
|
||||||
|
MediaContract.TVShows.EPISODE,
|
||||||
|
MediaContract.TVShows.WATCHEDEPISODES,
|
||||||
|
MediaContract.TVShows.RATING,
|
||||||
|
MediaContract.TVShows.PLOT,
|
||||||
|
MediaContract.TVShows.PLAYCOUNT,
|
||||||
|
MediaContract.TVShows.IMDBNUMBER,
|
||||||
|
MediaContract.TVShows.GENRES,
|
||||||
|
MediaContract.SyncColumns.UPDATED,
|
||||||
|
};
|
||||||
|
|
||||||
|
int ID = 0;
|
||||||
|
int TITLE = 1;
|
||||||
|
int THUMBNAIL = 2;
|
||||||
|
int FANART = 3;
|
||||||
|
int PREMIERED = 4;
|
||||||
|
int STUDIO = 5;
|
||||||
|
int EPISODE = 6;
|
||||||
|
int WATCHEDEPISODES = 7;
|
||||||
|
int RATING = 8;
|
||||||
|
int PLOT = 9;
|
||||||
|
int PLAYCOUNT = 10;
|
||||||
|
int IMDBNUMBER = 11;
|
||||||
|
int GENRES = 12;
|
||||||
|
int UPDATED = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seasons list query parameters.
|
||||||
|
*/
|
||||||
|
private interface SeasonsListQuery {
|
||||||
|
String[] PROJECTION = {
|
||||||
|
BaseColumns._ID,
|
||||||
|
MediaContract.Seasons.SEASON,
|
||||||
|
MediaContract.Seasons.THUMBNAIL,
|
||||||
|
MediaContract.Seasons.EPISODE,
|
||||||
|
MediaContract.Seasons.WATCHEDEPISODES
|
||||||
|
};
|
||||||
|
|
||||||
|
String SORT = MediaContract.Seasons.SEASON + " ASC";
|
||||||
|
|
||||||
|
int ID = 0;
|
||||||
|
int SEASON = 1;
|
||||||
|
int THUMBNAIL = 2;
|
||||||
|
int EPISODE = 3;
|
||||||
|
int WATCHEDEPISODES = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movie cast list query parameters.
|
||||||
|
*/
|
||||||
|
public interface TVShowCastListQuery {
|
||||||
|
String[] PROJECTION = {
|
||||||
|
BaseColumns._ID,
|
||||||
|
MediaContract.TVShowCast.NAME,
|
||||||
|
MediaContract.TVShowCast.ORDER,
|
||||||
|
MediaContract.TVShowCast.ROLE,
|
||||||
|
MediaContract.TVShowCast.THUMBNAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
String SORT = MediaContract.TVShowCast.ORDER + " ASC";
|
||||||
|
|
||||||
|
int ID = 0;
|
||||||
|
int NAME = 1;
|
||||||
|
int ORDER = 2;
|
||||||
|
int ROLE = 3;
|
||||||
|
int THUMBNAIL = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.xbmc.kore.ui;
|
package org.xbmc.kore.ui;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
@ -25,198 +25,128 @@ import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.CursorTreeAdapter;
|
|
||||||
import android.widget.ExpandableListView;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
import org.xbmc.kore.Settings;
|
import org.xbmc.kore.Settings;
|
||||||
|
import org.xbmc.kore.host.HostInfo;
|
||||||
import org.xbmc.kore.host.HostManager;
|
import org.xbmc.kore.host.HostManager;
|
||||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
|
||||||
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
||||||
import org.xbmc.kore.provider.MediaContract;
|
import org.xbmc.kore.provider.MediaContract;
|
||||||
import org.xbmc.kore.service.library.LibrarySyncService;
|
import org.xbmc.kore.service.library.LibrarySyncService;
|
||||||
import org.xbmc.kore.utils.LogUtils;
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
import org.xbmc.kore.utils.MediaPlayerUtils;
|
import org.xbmc.kore.utils.MediaPlayerUtils;
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
import org.xbmc.kore.utils.UIUtils;
|
||||||
|
import org.xbmc.kore.utils.Utils;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.InjectView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presents a list of episodes for a TV show
|
* Presents a list of episodes for a TV show season
|
||||||
*/
|
*/
|
||||||
public class TVShowEpisodeListFragment extends AbstractDetailsFragment
|
public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
private static final String TAG = LogUtils.makeLogTag(TVShowEpisodeListFragment.class);
|
private static final String TAG = LogUtils.makeLogTag(TVShowEpisodeListFragment.class);
|
||||||
|
|
||||||
public interface OnEpisodeSelectedListener {
|
public interface OnEpisodeSelectedListener {
|
||||||
public void onEpisodeSelected(int tvshowId, int episodeId);
|
void onEpisodeSelected(EpisodeViewHolder vh);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String TVSHOWID = "tvshow_id";
|
public static final String TVSHOWID = "tvshow_id";
|
||||||
public static final String SEASON = "season";
|
public static final String TVSHOWSEASON = "season";
|
||||||
|
|
||||||
private final String BUNDLE_SAVEDINSTANCE_LISTPOSITION = "lposition";
|
|
||||||
private final String BUNDLE_SAVEDINSTANCE_ITEMPOSITION = "iposition";
|
|
||||||
private final String BUNDLE_SAVEDINSTANCE_GROUPSEXPANDED = "groupsexpanded";
|
|
||||||
|
|
||||||
// Loader IDs. Must be -1 to differentiate from group position
|
|
||||||
private static final int LOADER_SEASONS = -1;
|
|
||||||
|
|
||||||
private int listPosition = 0;
|
|
||||||
private int itemPosition = 0;
|
|
||||||
|
|
||||||
private HashMap<Integer, Boolean> groupsExpanded = new HashMap<>();
|
|
||||||
private HashMap<Integer, Boolean> childCursorsLoading;
|
|
||||||
private boolean isReturning; // used to determine if we are returning to this fragment and need to restore the state
|
|
||||||
|
|
||||||
// Displayed show id
|
// Displayed show id
|
||||||
private int tvshowId = -1;
|
private int tvshowId = -1;
|
||||||
|
private int tvshowSeason = -1;
|
||||||
|
|
||||||
// Activity listener
|
// Activity listener
|
||||||
private OnEpisodeSelectedListener listenerActivity;
|
private OnEpisodeSelectedListener listenerActivity;
|
||||||
|
|
||||||
private SeasonsEpisodesAdapter adapter;
|
/**
|
||||||
|
* Create a new instance of this, initialized to show tvshowId
|
||||||
|
*/
|
||||||
|
@TargetApi(21)
|
||||||
|
public static TVShowEpisodeListFragment newInstance(int tvshowId, int season) {
|
||||||
|
TVShowEpisodeListFragment fragment = new TVShowEpisodeListFragment();
|
||||||
|
|
||||||
@InjectView(R.id.list) ExpandableListView seasonsEpisodesListView;
|
Bundle args = new Bundle();
|
||||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
args.putInt(TVSHOWID, tvshowId);
|
||||||
@InjectView(android.R.id.empty) TextView emptyView;
|
args.putInt(TVSHOWSEASON, season);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View createView(LayoutInflater inflater, ViewGroup container) {
|
protected String getListSyncType() { return LibrarySyncService.SYNC_SINGLE_TVSHOW; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSyncID() { return LibrarySyncService.SYNC_TVSHOWID; };
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSyncItemID() { return tvshowId; };
|
||||||
|
|
||||||
|
@TargetApi(16) @Nullable @Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
tvshowId = getArguments().getInt(TVSHOWID, -1);
|
tvshowId = getArguments().getInt(TVSHOWID, -1);
|
||||||
if (tvshowId == -1) {
|
tvshowSeason = getArguments().getInt(TVSHOWSEASON, -1);
|
||||||
|
if ((tvshowId == -1) || (tvshowSeason == -1)) {
|
||||||
// There's nothing to show
|
// There's nothing to show
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_tvshow_episodes_list, container, false);
|
|
||||||
ButterKnife.inject(this, root);
|
|
||||||
|
|
||||||
// Configure the adapter and start the loader
|
|
||||||
adapter = new SeasonsEpisodesAdapter(getActivity());
|
|
||||||
seasonsEpisodesListView.setAdapter(adapter);
|
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSyncType() {
|
protected void onListItemClicked(View view) {
|
||||||
return LibrarySyncService.SYNC_SINGLE_TVSHOW;
|
// Get the movie id from the tag
|
||||||
|
EpisodeViewHolder tag = (EpisodeViewHolder) view.getTag();
|
||||||
|
// Notify the activity
|
||||||
|
listenerActivity.onEpisodeSelected(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CursorAdapter createAdapter() {
|
||||||
|
return new SeasonsEpisodesAdapter(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSyncID() {
|
protected CursorLoader createCursorLoader() {
|
||||||
return LibrarySyncService.SYNC_TVSHOWID;
|
HostInfo hostInfo = HostManager.getInstance(getActivity()).getHostInfo();
|
||||||
}
|
Uri uri = MediaContract.Episodes.buildTVShowSeasonEpisodesListUri(hostInfo.getId(), tvshowId, tvshowSeason);
|
||||||
|
|
||||||
@Override
|
// Filters
|
||||||
protected int getSyncItemID() {
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
return tvshowId;
|
StringBuilder selection = new StringBuilder();
|
||||||
}
|
if (preferences.getBoolean(Settings.KEY_PREF_TVSHOWS_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_TVSHOWS_FILTER_HIDE_WATCHED)) {
|
||||||
|
selection.append(MediaContract.EpisodesColumns.PLAYCOUNT)
|
||||||
@Override
|
.append("=0");
|
||||||
protected SwipeRefreshLayout getSwipeRefreshLayout() {
|
|
||||||
return swipeRefreshLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDownload() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated (Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
seasonsEpisodesListView.setEmptyView(emptyView);
|
|
||||||
seasonsEpisodesListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
|
|
||||||
if (parent.isGroupExpanded(groupPosition)) {
|
|
||||||
parent.collapseGroup(groupPosition);
|
|
||||||
groupsExpanded.remove(groupPosition);
|
|
||||||
} else {
|
|
||||||
parent.expandGroup(groupPosition);
|
|
||||||
groupsExpanded.put(groupPosition, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
seasonsEpisodesListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
|
|
||||||
// Get the movie id from the tag
|
|
||||||
EpisodeViewHolder tag = (EpisodeViewHolder) v.getTag();
|
|
||||||
// Notify the activity
|
|
||||||
listenerActivity.onEpisodeSelected(tvshowId, tag.episodeId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
listPosition = savedInstanceState.getInt(BUNDLE_SAVEDINSTANCE_LISTPOSITION, 0);
|
|
||||||
itemPosition = savedInstanceState.getInt(BUNDLE_SAVEDINSTANCE_ITEMPOSITION, 0);
|
|
||||||
groupsExpanded = (HashMap) savedInstanceState.getSerializable(BUNDLE_SAVEDINSTANCE_GROUPSEXPANDED);
|
|
||||||
isReturning = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initLoader();
|
return new CursorLoader(getActivity(), uri,
|
||||||
|
EpisodesListQuery.PROJECTION, selection.toString(), null, EpisodesListQuery.SORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(activity);
|
super.onAttach(context);
|
||||||
try {
|
try {
|
||||||
listenerActivity = (OnEpisodeSelectedListener) activity;
|
listenerActivity = (OnEpisodeSelectedListener) context;
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
throw new ClassCastException(activity.toString() + " must implement OnEpisodeSelectedListener");
|
throw new ClassCastException(context.toString() + " must implement OnEpisodeSelectedListener");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
//Save scroll position
|
|
||||||
listPosition = seasonsEpisodesListView.getFirstVisiblePosition();
|
|
||||||
View itemView = seasonsEpisodesListView.getChildAt(0);
|
|
||||||
if (itemView != null) {
|
|
||||||
itemPosition = itemView.getTop();
|
|
||||||
} else {
|
|
||||||
itemPosition = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
isReturning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
|
|
||||||
outState.putInt(BUNDLE_SAVEDINSTANCE_ITEMPOSITION, itemPosition);
|
|
||||||
outState.putInt(BUNDLE_SAVEDINSTANCE_LISTPOSITION, listPosition);
|
|
||||||
outState.putSerializable(BUNDLE_SAVEDINSTANCE_GROUPSEXPANDED, groupsExpanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetach() {
|
public void onDetach() {
|
||||||
super.onDetach();
|
super.onDetach();
|
||||||
|
@ -250,7 +180,7 @@ public class TVShowEpisodeListFragment extends AbstractDetailsFragment
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
.putBoolean(Settings.KEY_PREF_TVSHOW_EPISODES_FILTER_HIDE_WATCHED, item.isChecked())
|
.putBoolean(Settings.KEY_PREF_TVSHOW_EPISODES_FILTER_HIDE_WATCHED, item.isChecked())
|
||||||
.apply();
|
.apply();
|
||||||
getLoaderManager().restartLoader(LOADER_SEASONS, null, this);
|
refreshList();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -259,161 +189,6 @@ public class TVShowEpisodeListFragment extends AbstractDetailsFragment
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
|
||||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
|
||||||
getLoaderManager().restartLoader(LOADER_SEASONS, null, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loader callbacks
|
|
||||||
*/
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
|
|
||||||
if (!isAdded()) {
|
|
||||||
LogUtils.LOGD(TAG, "Trying to create a loader, but the fragment isn't added. " +
|
|
||||||
"Loader Id: " + id);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri uri;
|
|
||||||
StringBuilder selection = new StringBuilder();
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
|
||||||
boolean tvshowEpisodesFilterHideWatched =
|
|
||||||
preferences.getBoolean(Settings.KEY_PREF_TVSHOW_EPISODES_FILTER_HIDE_WATCHED, Settings.DEFAULT_PREF_TVSHOW_EPISODES_FILTER_HIDE_WATCHED);
|
|
||||||
switch (id) {
|
|
||||||
case LOADER_SEASONS:
|
|
||||||
// Load seasons
|
|
||||||
uri = MediaContract.Seasons.buildTVShowSeasonsListUri(getHostInfo().getId(), tvshowId);
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (tvshowEpisodesFilterHideWatched) {
|
|
||||||
selection.append(MediaContract.SeasonsColumns.WATCHEDEPISODES)
|
|
||||||
.append("!=")
|
|
||||||
.append(MediaContract.SeasonsColumns.EPISODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CursorLoader(getActivity(), uri,
|
|
||||||
SeasonsListQuery.PROJECTION, selection.toString(), null, SeasonsListQuery.SORT);
|
|
||||||
default:
|
|
||||||
// Load episodes for a season. Season is in bundle
|
|
||||||
int season = bundle.getInt(SEASON);
|
|
||||||
uri = MediaContract.Episodes.buildTVShowSeasonEpisodesListUri(getHostInfo().getId(), tvshowId, season);
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (tvshowEpisodesFilterHideWatched) {
|
|
||||||
selection.append(MediaContract.EpisodesColumns.PLAYCOUNT)
|
|
||||||
.append("=0");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CursorLoader(getActivity(), uri,
|
|
||||||
EpisodesListQuery.PROJECTION, selection.toString(), null, EpisodesListQuery.SORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
|
||||||
switch (cursorLoader.getId()) {
|
|
||||||
case LOADER_SEASONS:
|
|
||||||
adapter.setGroupCursor(cursor);
|
|
||||||
setState(cursor);
|
|
||||||
// To prevent the empty text from appearing on the first load, set it now
|
|
||||||
emptyView.setText(getString(R.string.no_episodes_found));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Check if the group cursor is set before setting the children cursor
|
|
||||||
// Somehow, when popping the back stack, the children cursor are reloaded first...
|
|
||||||
if (adapter.getCursor() != null) {
|
|
||||||
int id = cursorLoader.getId();
|
|
||||||
adapter.setChildrenCursor(id, cursor);
|
|
||||||
childCursorsLoading.remove(id);
|
|
||||||
if (isReturning && childCursorsLoading.isEmpty()) {
|
|
||||||
isReturning = false;
|
|
||||||
//All previous expanded child cursors loaded. Now we can finally restore the list position
|
|
||||||
seasonsEpisodesListView.setSelectionFromTop(listPosition, itemPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
|
||||||
switch (cursorLoader.getId()) {
|
|
||||||
case LOADER_SEASONS:
|
|
||||||
adapter.setGroupCursor(null);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Check if the group cursor is set before setting the children cursor
|
|
||||||
// Somehow, when popping the back stack, the children cursor are reloaded first...
|
|
||||||
if (adapter.getCursor() != null) {
|
|
||||||
try {
|
|
||||||
adapter.setChildrenCursor(cursorLoader.getId(), null);
|
|
||||||
} catch (NullPointerException exc) {
|
|
||||||
// Errrr... Adapter expired?
|
|
||||||
LogUtils.LOGW(TAG, "Adapter expired.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setState(Cursor cursor) {
|
|
||||||
if (cursor.getCount() == 1) {
|
|
||||||
seasonsEpisodesListView.expandGroup(0);
|
|
||||||
} else if (cursor.getCount() > 0) {
|
|
||||||
cursor.moveToFirst();
|
|
||||||
do {
|
|
||||||
int position = cursor.getPosition();
|
|
||||||
|
|
||||||
// Expand the first season that has unseen episodes
|
|
||||||
int unwatched = cursor.getInt(SeasonsListQuery.EPISODE) - cursor.getInt(SeasonsListQuery.WATCHEDEPISODES);
|
|
||||||
if (groupsExpanded.isEmpty() && (unwatched > 0)) {
|
|
||||||
seasonsEpisodesListView.expandGroup(position);
|
|
||||||
groupsExpanded.put(position, true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupsExpanded.get(position) != null) {
|
|
||||||
seasonsEpisodesListView.expandGroup(position);
|
|
||||||
}
|
|
||||||
} while (cursor.moveToNext());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initLoader() {
|
|
||||||
childCursorsLoading = new HashMap<>();
|
|
||||||
for (int id : groupsExpanded.keySet()) {
|
|
||||||
childCursorsLoading.put(id, true);
|
|
||||||
}
|
|
||||||
getLoaderManager().initLoader(LOADER_SEASONS, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Seasons list query parameters.
|
|
||||||
*/
|
|
||||||
private interface SeasonsListQuery {
|
|
||||||
String[] PROJECTION = {
|
|
||||||
BaseColumns._ID,
|
|
||||||
MediaContract.Seasons.SEASON,
|
|
||||||
MediaContract.Seasons.THUMBNAIL,
|
|
||||||
MediaContract.Seasons.EPISODE,
|
|
||||||
MediaContract.Seasons.WATCHEDEPISODES
|
|
||||||
};
|
|
||||||
|
|
||||||
String SORT = MediaContract.Seasons.SEASON + " ASC";
|
|
||||||
|
|
||||||
final int ID = 0;
|
|
||||||
final int SEASON = 1;
|
|
||||||
final int THUMBNAIL = 2;
|
|
||||||
final int EPISODE = 3;
|
|
||||||
final int WATCHEDEPISODES = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Episodes list query parameters.
|
* Episodes list query parameters.
|
||||||
*/
|
*/
|
||||||
|
@ -431,111 +206,76 @@ public class TVShowEpisodeListFragment extends AbstractDetailsFragment
|
||||||
|
|
||||||
String SORT = MediaContract.Episodes.EPISODE + " ASC";
|
String SORT = MediaContract.Episodes.EPISODE + " ASC";
|
||||||
|
|
||||||
final int ID = 0;
|
int ID = 0;
|
||||||
final int EPISODEID = 1;
|
int EPISODEID = 1;
|
||||||
final int EPISODE = 2;
|
int EPISODE = 2;
|
||||||
final int THUMBNAIL = 3;
|
int THUMBNAIL = 3;
|
||||||
final int PLAYCOUNT = 4;
|
int PLAYCOUNT = 4;
|
||||||
final int TITLE = 5;
|
int TITLE = 5;
|
||||||
final int RUNTIME = 6;
|
int RUNTIME = 6;
|
||||||
final int FIRSTAIRED = 7;
|
int FIRSTAIRED = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter for the {@link android.widget.ExpandableListView}
|
|
||||||
* Manages the seasons and episodes list
|
|
||||||
*/
|
|
||||||
private class SeasonsEpisodesAdapter extends CursorTreeAdapter {
|
|
||||||
|
|
||||||
private int themeAccentColor;
|
private class SeasonsEpisodesAdapter extends CursorAdapter {
|
||||||
private int iconCollapseResId,
|
|
||||||
iconExpandResId;
|
|
||||||
|
|
||||||
private HostManager hostManager;
|
private HostManager hostManager;
|
||||||
private int artWidth, artHeight;
|
private int artWidth, artHeight;
|
||||||
|
private int themeAccentColor;
|
||||||
|
|
||||||
public SeasonsEpisodesAdapter(Context context) {
|
public SeasonsEpisodesAdapter(Context context) {
|
||||||
// Cursor will be set vir CursorLoader
|
super(context, null, false);
|
||||||
super(null, context);
|
|
||||||
|
|
||||||
// Get the default accent color
|
// Get the default accent color
|
||||||
Resources.Theme theme = context.getTheme();
|
Resources.Theme theme = context.getTheme();
|
||||||
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
|
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
|
||||||
R.attr.colorAccent,
|
R.attr.colorAccent
|
||||||
R.attr.iconCollapse,
|
|
||||||
R.attr.iconExpand,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
themeAccentColor = styledAttributes.getColor(styledAttributes.getIndex(0), getResources().getColor(R.color.accent_default));
|
themeAccentColor = styledAttributes.getColor(styledAttributes.getIndex(0), getResources().getColor(R.color.accent_default));
|
||||||
iconCollapseResId = styledAttributes.getResourceId(styledAttributes.getIndex(1), R.drawable.ic_expand_less_white_24dp);
|
|
||||||
iconExpandResId = styledAttributes.getResourceId(styledAttributes.getIndex(2), R.drawable.ic_expand_more_white_24dp);
|
|
||||||
styledAttributes.recycle();
|
styledAttributes.recycle();
|
||||||
|
|
||||||
this.hostManager = HostManager.getInstance(context);
|
this.hostManager = HostManager.getInstance(context);
|
||||||
|
|
||||||
// Get the art dimensions
|
// Get the art dimensions
|
||||||
Resources resources = context.getResources();
|
Resources resources = context.getResources();
|
||||||
artWidth = (int)(resources.getDimension(R.dimen.seasonlist_art_width) /
|
artWidth = (int)(resources.getDimension(R.dimen.episodelist_art_width) /
|
||||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||||
artHeight = (int)(resources.getDimension(R.dimen.seasonlist_art_heigth) /
|
artHeight = (int)(resources.getDimension(R.dimen.episodelist_art_heigth) /
|
||||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
|
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
|
||||||
return LayoutInflater.from(context).inflate(R.layout.list_item_season, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
|
|
||||||
final View view = LayoutInflater.from(context)
|
final View view = LayoutInflater.from(context)
|
||||||
.inflate(R.layout.list_item_episode, parent, false);
|
.inflate(R.layout.list_item_episode, parent, false);
|
||||||
|
|
||||||
// Setup View holder pattern
|
// Setup View holder pattern
|
||||||
EpisodeViewHolder viewHolder = new EpisodeViewHolder();
|
EpisodeViewHolder viewHolder = new EpisodeViewHolder();
|
||||||
viewHolder.container = (RelativeLayout)view.findViewById(R.id.container);
|
|
||||||
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
|
viewHolder.titleView = (TextView)view.findViewById(R.id.title);
|
||||||
viewHolder.detailsView = (TextView)view.findViewById(R.id.details);
|
viewHolder.detailsView = (TextView)view.findViewById(R.id.details);
|
||||||
viewHolder.episodenumberView = (TextView)view.findViewById(R.id.episode_number);
|
viewHolder.episodenumberView = (TextView)view.findViewById(R.id.episode_number);
|
||||||
viewHolder.contextMenuView = (ImageView)view.findViewById(R.id.list_context_menu);
|
viewHolder.contextMenuView = (ImageView)view.findViewById(R.id.list_context_menu);
|
||||||
viewHolder.checkmarkView = (ImageView)view.findViewById(R.id.checkmark);
|
viewHolder.checkmarkView = (ImageView)view.findViewById(R.id.checkmark);
|
||||||
|
viewHolder.artView = (ImageView)view.findViewById(R.id.art);
|
||||||
|
|
||||||
view.setTag(viewHolder);
|
view.setTag(viewHolder);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
TextView seasonView = (TextView)view.findViewById(R.id.season);
|
|
||||||
TextView episodesView = (TextView)view.findViewById(R.id.episodes);
|
|
||||||
ImageView artView = (ImageView)view.findViewById(R.id.art);
|
|
||||||
|
|
||||||
seasonView.setText(String.format(context.getString(R.string.season_number),
|
|
||||||
cursor.getInt(SeasonsListQuery.SEASON)));
|
|
||||||
int numEpisodes = cursor.getInt(SeasonsListQuery.EPISODE),
|
|
||||||
watchedEpisodes = cursor.getInt(SeasonsListQuery.WATCHEDEPISODES);
|
|
||||||
episodesView.setText(String.format(context.getString(R.string.num_episodes),
|
|
||||||
numEpisodes, numEpisodes - watchedEpisodes));
|
|
||||||
|
|
||||||
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
|
|
||||||
cursor.getString(SeasonsListQuery.THUMBNAIL),
|
|
||||||
String.valueOf(cursor.getInt(SeasonsListQuery.SEASON)),
|
|
||||||
artView, artWidth, artHeight);
|
|
||||||
|
|
||||||
ImageView indicator = (ImageView)view.findViewById(R.id.status_indicator);
|
|
||||||
if (isExpanded) {
|
|
||||||
indicator.setImageResource(iconCollapseResId);
|
|
||||||
} else {
|
|
||||||
indicator.setImageResource(iconExpandResId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
|
|
||||||
final EpisodeViewHolder viewHolder = (EpisodeViewHolder)view.getTag();
|
final EpisodeViewHolder viewHolder = (EpisodeViewHolder)view.getTag();
|
||||||
|
|
||||||
// Save the episode id
|
// Save the episode id
|
||||||
viewHolder.episodeId = cursor.getInt(EpisodesListQuery.EPISODEID);
|
viewHolder.episodeId = cursor.getInt(EpisodesListQuery.EPISODEID);
|
||||||
|
viewHolder.title = cursor.getString(EpisodesListQuery.TITLE);
|
||||||
|
|
||||||
|
if(Utils.isLollipopOrLater()) {
|
||||||
|
viewHolder.artView.setTransitionName("a"+viewHolder.episodeId);
|
||||||
|
}
|
||||||
|
|
||||||
viewHolder.episodenumberView.setText(
|
viewHolder.episodenumberView.setText(
|
||||||
String.format(context.getString(R.string.episode_number),
|
String.format(context.getString(R.string.episode_number),
|
||||||
|
@ -555,49 +295,30 @@ public class TVShowEpisodeListFragment extends AbstractDetailsFragment
|
||||||
viewHolder.checkmarkView.setVisibility(View.INVISIBLE);
|
viewHolder.checkmarkView.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
|
||||||
|
cursor.getString(EpisodesListQuery.THUMBNAIL), viewHolder.title,
|
||||||
|
viewHolder.artView, artWidth, artHeight);
|
||||||
|
|
||||||
// For the popupmenu
|
// For the popupmenu
|
||||||
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
|
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
|
||||||
contextMenu.setTag(viewHolder);
|
contextMenu.setTag(viewHolder);
|
||||||
contextMenu.setOnClickListener(contextlistItemMenuClickListener);
|
contextMenu.setOnClickListener(contextlistItemMenuClickListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor getChildrenCursor(Cursor groupCursor) {
|
|
||||||
// Check if the fragment is attached to avoid IllegalStateException...
|
|
||||||
if (!isAdded()) return null;
|
|
||||||
|
|
||||||
// Start the episodes loader
|
|
||||||
final int season = groupCursor.getInt(SeasonsListQuery.SEASON);
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putInt(SEASON, season);
|
|
||||||
int groupPositon = groupCursor.getPosition();
|
|
||||||
// The season id will be passed in a bundle to the loadermanager, and the group id
|
|
||||||
// will be used as the loader's id
|
|
||||||
LoaderManager loaderManager = getLoaderManager();
|
|
||||||
if ((loaderManager.getLoader(groupPositon) == null) ||
|
|
||||||
(loaderManager.getLoader(groupPositon).isReset())) {
|
|
||||||
loaderManager.initLoader(groupPositon, bundle, TVShowEpisodeListFragment.this);
|
|
||||||
} else {
|
|
||||||
loaderManager.restartLoader(groupPositon, bundle, TVShowEpisodeListFragment.this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View holder pattern, only for episodes
|
* View holder pattern, only for episodes
|
||||||
*/
|
*/
|
||||||
private static class EpisodeViewHolder {
|
public static class EpisodeViewHolder {
|
||||||
RelativeLayout container;
|
|
||||||
TextView titleView;
|
TextView titleView;
|
||||||
TextView detailsView;
|
TextView detailsView;
|
||||||
TextView episodenumberView;
|
TextView episodenumberView;
|
||||||
ImageView contextMenuView;
|
ImageView contextMenuView;
|
||||||
ImageView checkmarkView;
|
ImageView checkmarkView;
|
||||||
// ImageView artView;
|
ImageView artView;
|
||||||
|
|
||||||
int episodeId;
|
int episodeId;
|
||||||
|
String title;
|
||||||
}
|
}
|
||||||
|
|
||||||
private View.OnClickListener contextlistItemMenuClickListener = new View.OnClickListener() {
|
private View.OnClickListener contextlistItemMenuClickListener = new View.OnClickListener() {
|
||||||
|
|
|
@ -1,419 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 Synced Synapse. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 org.xbmc.kore.ui;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.BaseColumns;
|
|
||||||
import android.support.v4.app.LoaderManager;
|
|
||||||
import android.support.v4.content.CursorLoader;
|
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewTreeObserver;
|
|
||||||
import android.widget.GridLayout;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ScrollView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
|
||||||
import org.xbmc.kore.Settings;
|
|
||||||
import org.xbmc.kore.jsonrpc.event.MediaSyncEvent;
|
|
||||||
import org.xbmc.kore.jsonrpc.type.VideoType;
|
|
||||||
import org.xbmc.kore.provider.MediaContract;
|
|
||||||
import org.xbmc.kore.service.library.LibrarySyncService;
|
|
||||||
import org.xbmc.kore.utils.LogUtils;
|
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
|
||||||
import org.xbmc.kore.utils.Utils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.InjectView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Presents a TV Show overview
|
|
||||||
*/
|
|
||||||
public class TVShowOverviewFragment extends AbstractDetailsFragment
|
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
private static final String TAG = LogUtils.makeLogTag(TVShowOverviewFragment.class);
|
|
||||||
|
|
||||||
public static final String TVSHOWID = "tvshow_id";
|
|
||||||
|
|
||||||
// Loader IDs
|
|
||||||
private static final int LOADER_TVSHOW = 0,
|
|
||||||
LOADER_CAST = 1;
|
|
||||||
|
|
||||||
// Displayed movie id
|
|
||||||
private int tvshowId = -1;
|
|
||||||
private String tvshowTitle;
|
|
||||||
|
|
||||||
private ArrayList<VideoType.Cast> castArrayList;
|
|
||||||
|
|
||||||
// Controls whether a automatic sync refresh has been issued for this show
|
|
||||||
private static boolean hasIssuedOutdatedRefresh = false;
|
|
||||||
|
|
||||||
@InjectView(R.id.swipe_refresh_layout) SwipeRefreshLayout swipeRefreshLayout;
|
|
||||||
|
|
||||||
// // Buttons
|
|
||||||
// @InjectView(R.id.go_to_imdb) ImageButton imdbButton;
|
|
||||||
|
|
||||||
// Detail views
|
|
||||||
@InjectView(R.id.media_panel) ScrollView mediaPanel;
|
|
||||||
|
|
||||||
@InjectView(R.id.art) ImageView mediaArt;
|
|
||||||
@InjectView(R.id.poster) ImageView mediaPoster;
|
|
||||||
|
|
||||||
@InjectView(R.id.media_title) TextView mediaTitle;
|
|
||||||
@InjectView(R.id.media_undertitle) TextView mediaUndertitle;
|
|
||||||
|
|
||||||
@InjectView(R.id.rating) TextView mediaRating;
|
|
||||||
@InjectView(R.id.max_rating) TextView mediaMaxRating;
|
|
||||||
@InjectView(R.id.premiered) TextView mediaPremiered;
|
|
||||||
@InjectView(R.id.genres) TextView mediaGenres;
|
|
||||||
|
|
||||||
@InjectView(R.id.media_description) TextView mediaDescription;
|
|
||||||
@InjectView(R.id.cast_list) GridLayout videoCastList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of this, initialized to show tvshowId
|
|
||||||
*/
|
|
||||||
public static TVShowOverviewFragment newInstance(int tvshowId) {
|
|
||||||
TVShowOverviewFragment fragment = new TVShowOverviewFragment();
|
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putInt(TVSHOWID, tvshowId);
|
|
||||||
fragment.setArguments(args);
|
|
||||||
return fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@TargetApi(21)
|
|
||||||
protected View createView(LayoutInflater inflater, ViewGroup container) {
|
|
||||||
Bundle bundle = getArguments();
|
|
||||||
tvshowId = bundle.getInt(TVShowDetailsFragment.BUNDLE_KEY_TVSHOWID, -1);
|
|
||||||
|
|
||||||
if (tvshowId == -1) {
|
|
||||||
// There's nothing to show
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_tvshow_overview, container, false);
|
|
||||||
ButterKnife.inject(this, root);
|
|
||||||
|
|
||||||
//UIUtils.setSwipeRefreshLayoutColorScheme(swipeRefreshLayout);
|
|
||||||
|
|
||||||
// Setup dim the fanart when scroll changes. Full dim on 4 * iconSize dp
|
|
||||||
Resources resources = getActivity().getResources();
|
|
||||||
final int pixelsToTransparent = 4 * resources.getDimensionPixelSize(R.dimen.default_icon_size);
|
|
||||||
mediaPanel.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
|
|
||||||
@Override
|
|
||||||
public void onScrollChanged() {
|
|
||||||
float y = mediaPanel.getScrollY();
|
|
||||||
float newAlpha = Math.min(1, Math.max(0, 1 - (y / pixelsToTransparent)));
|
|
||||||
mediaArt.setAlpha(newAlpha);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tvshowTitle = bundle.getString(TVShowDetailsFragment.BUNDLE_KEY_TITLE);
|
|
||||||
|
|
||||||
mediaTitle.setText(tvshowTitle);
|
|
||||||
setMediaUndertitle(bundle.getInt(TVShowDetailsFragment.BUNDLE_KEY_EPISODE), bundle.getInt(TVShowDetailsFragment.BUNDLE_KEY_WATCHEDEPISODES));
|
|
||||||
setMediaPremiered(bundle.getString(TVShowDetailsFragment.BUNDLE_KEY_PREMIERED), bundle.getString(TVShowDetailsFragment.BUNDLE_KEY_STUDIO));
|
|
||||||
mediaGenres.setText(bundle.getString(TVShowDetailsFragment.BUNDLE_KEY_GENRES));
|
|
||||||
setMediaRating(bundle.getDouble(TVShowDetailsFragment.BUNDLE_KEY_RATING));
|
|
||||||
mediaDescription.setText(bundle.getString(TVShowDetailsFragment.BUNDLE_KEY_PLOT));
|
|
||||||
|
|
||||||
if(Utils.isLollipopOrLater()) {
|
|
||||||
mediaPoster.setTransitionName(getArguments().getString(TVShowDetailsFragment.POSTER_TRANS_NAME));
|
|
||||||
}
|
|
||||||
// Pad main content view to overlap with bottom system bar
|
|
||||||
// UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true);
|
|
||||||
// mediaPanel.setClipToPadding(false);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSyncType() {
|
|
||||||
return LibrarySyncService.SYNC_SINGLE_TVSHOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSyncID() {
|
|
||||||
return LibrarySyncService.SYNC_TVSHOWID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getSyncItemID() {
|
|
||||||
return tvshowId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SwipeRefreshLayout getSwipeRefreshLayout() {
|
|
||||||
return swipeRefreshLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDownload() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated (Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
hasIssuedOutdatedRefresh = false;
|
|
||||||
|
|
||||||
// Start the loaders
|
|
||||||
getLoaderManager().initLoader(LOADER_TVSHOW, null, this);
|
|
||||||
getLoaderManager().initLoader(LOADER_CAST, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
|
||||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
|
||||||
getLoaderManager().restartLoader(LOADER_TVSHOW, null, this);
|
|
||||||
getLoaderManager().restartLoader(LOADER_CAST, null, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loader callbacks
|
|
||||||
*/
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
|
||||||
Uri uri;
|
|
||||||
switch (i) {
|
|
||||||
case LOADER_TVSHOW:
|
|
||||||
uri = MediaContract.TVShows.buildTVShowUri(getHostInfo().getId(), tvshowId);
|
|
||||||
return new CursorLoader(getActivity(), uri,
|
|
||||||
TVShowDetailsQuery.PROJECTION, null, null, null);
|
|
||||||
case LOADER_CAST:
|
|
||||||
uri = MediaContract.TVShowCast.buildTVShowCastListUri(getHostInfo().getId(), tvshowId);
|
|
||||||
return new CursorLoader(getActivity(), uri,
|
|
||||||
TVShowCastListQuery.PROJECTION, null, null, TVShowCastListQuery.SORT);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
|
|
||||||
LogUtils.LOGD(TAG, "onLoadFinished");
|
|
||||||
if (cursor != null && cursor.getCount() > 0) {
|
|
||||||
switch (cursorLoader.getId()) {
|
|
||||||
case LOADER_TVSHOW:
|
|
||||||
displayTVShowDetails(cursor);
|
|
||||||
checkOutdatedTVShowDetails(cursor);
|
|
||||||
break;
|
|
||||||
case LOADER_CAST:
|
|
||||||
displayCastList(cursor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Cursor> cursorLoader) {
|
|
||||||
// Release loader's data
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Callbacks for button bar
|
|
||||||
// */
|
|
||||||
// @OnClick(R.id.go_to_imdb)
|
|
||||||
// public void onImdbClicked(View v) {
|
|
||||||
// String imdbNumber = (String)v.getTag();
|
|
||||||
//
|
|
||||||
// if (imdbNumber != null) {
|
|
||||||
// Utils.openImdbForMovie(getActivity(), imdbNumber);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the tv show details
|
|
||||||
*
|
|
||||||
* @param cursor Cursor with the data
|
|
||||||
*/
|
|
||||||
private void displayTVShowDetails(Cursor cursor) {
|
|
||||||
LogUtils.LOGD(TAG, "displayTVShowDetails");
|
|
||||||
cursor.moveToFirst();
|
|
||||||
tvshowTitle = cursor.getString(TVShowDetailsQuery.TITLE);
|
|
||||||
mediaTitle.setText(tvshowTitle);
|
|
||||||
int numEpisodes = cursor.getInt(TVShowDetailsQuery.EPISODE),
|
|
||||||
watchedEpisodes = cursor.getInt(TVShowDetailsQuery.WATCHEDEPISODES);
|
|
||||||
setMediaUndertitle(numEpisodes, watchedEpisodes);
|
|
||||||
|
|
||||||
setMediaPremiered(cursor.getString(TVShowDetailsQuery.PREMIERED), cursor.getString(TVShowDetailsQuery.STUDIO));
|
|
||||||
|
|
||||||
mediaGenres.setText(cursor.getString(TVShowDetailsQuery.GENRES));
|
|
||||||
|
|
||||||
setMediaRating(cursor.getDouble(TVShowDetailsQuery.RATING));
|
|
||||||
|
|
||||||
mediaDescription.setText(cursor.getString(TVShowDetailsQuery.PLOT));
|
|
||||||
|
|
||||||
// // IMDB button
|
|
||||||
// imdbButton.setTag(cursor.getString(TVShowDetailsQuery.IMDBNUMBER));
|
|
||||||
|
|
||||||
// Images
|
|
||||||
Resources resources = getActivity().getResources();
|
|
||||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
|
||||||
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
|
||||||
|
|
||||||
int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
|
||||||
int posterHeight = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height);
|
|
||||||
UIUtils.loadImageWithCharacterAvatar(getActivity(), getHostManager(),
|
|
||||||
cursor.getString(TVShowDetailsQuery.THUMBNAIL), tvshowTitle,
|
|
||||||
mediaPoster, posterWidth, posterHeight);
|
|
||||||
int artHeight = resources.getDimensionPixelOffset(R.dimen.now_playing_art_height);
|
|
||||||
UIUtils.loadImageIntoImageview(getHostManager(),
|
|
||||||
cursor.getString(TVShowDetailsQuery.FANART),
|
|
||||||
mediaArt, displayMetrics.widthPixels, artHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMediaUndertitle(int numEpisodes, int watchedEpisodes) {
|
|
||||||
String episodes = String.format(getString(R.string.num_episodes),
|
|
||||||
numEpisodes, numEpisodes - watchedEpisodes);
|
|
||||||
mediaUndertitle.setText(episodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMediaPremiered(String premiered, String studio) {
|
|
||||||
mediaPremiered.setText(String.format(getString(R.string.premiered),
|
|
||||||
premiered) + " | " + studio);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMediaRating(double rating) {
|
|
||||||
if (rating > 0) {
|
|
||||||
mediaRating.setVisibility(View.VISIBLE);
|
|
||||||
mediaMaxRating.setVisibility(View.VISIBLE);
|
|
||||||
mediaRating.setText(String.format("%01.01f", rating));
|
|
||||||
mediaMaxRating.setText(getString(R.string.max_rating_video));
|
|
||||||
} else {
|
|
||||||
mediaRating.setVisibility(View.INVISIBLE);
|
|
||||||
mediaMaxRating.setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Display the cast details
|
|
||||||
*
|
|
||||||
* @param cursor Cursor with the data
|
|
||||||
*/
|
|
||||||
private void displayCastList(Cursor cursor) {
|
|
||||||
// Transform the cursor into a List<VideoType.Cast>
|
|
||||||
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
castArrayList = new ArrayList<VideoType.Cast>(cursor.getCount());
|
|
||||||
do {
|
|
||||||
castArrayList.add(new VideoType.Cast(cursor.getString(TVShowCastListQuery.NAME),
|
|
||||||
cursor.getInt(TVShowCastListQuery.ORDER),
|
|
||||||
cursor.getString(TVShowCastListQuery.ROLE),
|
|
||||||
cursor.getString(TVShowCastListQuery.THUMBNAIL)));
|
|
||||||
} while (cursor.moveToNext());
|
|
||||||
|
|
||||||
UIUtils.setupCastInfo(getActivity(), castArrayList, videoCastList,
|
|
||||||
AllCastActivity.buildLaunchIntent(getActivity(), tvshowTitle, castArrayList));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks wether we should refresh the TV Show details with the info on XBMC
|
|
||||||
* The details will be updated if the last update is older than what is configured in the
|
|
||||||
* settings
|
|
||||||
*
|
|
||||||
* @param cursor Cursor with the data
|
|
||||||
*/
|
|
||||||
private void checkOutdatedTVShowDetails(Cursor cursor) {
|
|
||||||
if (hasIssuedOutdatedRefresh)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cursor.moveToFirst();
|
|
||||||
long lastUpdated = cursor.getLong(TVShowDetailsQuery.UPDATED);
|
|
||||||
|
|
||||||
if (System.currentTimeMillis() > lastUpdated + Settings.DB_UPDATE_INTERVAL) {
|
|
||||||
// Trigger a silent refresh
|
|
||||||
hasIssuedOutdatedRefresh = true;
|
|
||||||
startSync(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TV Show details query parameters.
|
|
||||||
*/
|
|
||||||
private interface TVShowDetailsQuery {
|
|
||||||
String[] PROJECTION = {
|
|
||||||
BaseColumns._ID,
|
|
||||||
MediaContract.TVShows.TITLE,
|
|
||||||
MediaContract.TVShows.THUMBNAIL,
|
|
||||||
MediaContract.TVShows.FANART,
|
|
||||||
MediaContract.TVShows.PREMIERED,
|
|
||||||
MediaContract.TVShows.STUDIO,
|
|
||||||
MediaContract.TVShows.EPISODE,
|
|
||||||
MediaContract.TVShows.WATCHEDEPISODES,
|
|
||||||
MediaContract.TVShows.RATING,
|
|
||||||
MediaContract.TVShows.PLOT,
|
|
||||||
MediaContract.TVShows.PLAYCOUNT,
|
|
||||||
MediaContract.TVShows.IMDBNUMBER,
|
|
||||||
MediaContract.TVShows.GENRES,
|
|
||||||
MediaContract.SyncColumns.UPDATED,
|
|
||||||
};
|
|
||||||
|
|
||||||
final int ID = 0;
|
|
||||||
final int TITLE = 1;
|
|
||||||
final int THUMBNAIL = 2;
|
|
||||||
final int FANART = 3;
|
|
||||||
final int PREMIERED = 4;
|
|
||||||
final int STUDIO = 5;
|
|
||||||
final int EPISODE = 6;
|
|
||||||
final int WATCHEDEPISODES = 7;
|
|
||||||
final int RATING = 8;
|
|
||||||
final int PLOT = 9;
|
|
||||||
final int PLAYCOUNT = 10;
|
|
||||||
final int IMDBNUMBER = 11;
|
|
||||||
final int GENRES = 12;
|
|
||||||
final int UPDATED = 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Movie cast list query parameters.
|
|
||||||
*/
|
|
||||||
public interface TVShowCastListQuery {
|
|
||||||
String[] PROJECTION = {
|
|
||||||
BaseColumns._ID,
|
|
||||||
MediaContract.TVShowCast.NAME,
|
|
||||||
MediaContract.TVShowCast.ORDER,
|
|
||||||
MediaContract.TVShowCast.ROLE,
|
|
||||||
MediaContract.TVShowCast.THUMBNAIL,
|
|
||||||
};
|
|
||||||
|
|
||||||
String SORT = MediaContract.TVShowCast.ORDER + " ASC";
|
|
||||||
|
|
||||||
final int ID = 0;
|
|
||||||
final int NAME = 1;
|
|
||||||
final int ORDER = 2;
|
|
||||||
final int ROLE = 3;
|
|
||||||
final int THUMBNAIL = 4;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,18 +42,22 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class TVShowsActivity extends BaseActivity
|
public class TVShowsActivity extends BaseActivity
|
||||||
implements TVShowListFragment.OnTVShowSelectedListener,
|
implements TVShowListFragment.OnTVShowSelectedListener,
|
||||||
TVShowEpisodeListFragment.OnEpisodeSelectedListener {
|
TVShowDetailsFragment.OnSeasonSelectedListener,
|
||||||
|
TVShowEpisodeListFragment.OnEpisodeSelectedListener {
|
||||||
private static final String TAG = LogUtils.makeLogTag(TVShowsActivity.class);
|
private static final String TAG = LogUtils.makeLogTag(TVShowsActivity.class);
|
||||||
|
|
||||||
public static final String TVSHOWID = "tvshow_id";
|
public static final String TVSHOWID = "tvshow_id";
|
||||||
public static final String TVSHOWTITLE = "tvshow_title";
|
public static final String TVSHOWTITLE = "tvshow_title";
|
||||||
public static final String EPISODEID = "episode_id";
|
public static final String EPISODEID = "episode_id";
|
||||||
|
public static final String SEASON = "season";
|
||||||
|
public static final String SEASONTITLE = "season_title";
|
||||||
|
|
||||||
private int selectedTVShowId = -1;
|
private int selectedTVShowId = -1;
|
||||||
private String selectedTVShowTitle = null;
|
private String selectedTVShowTitle = null;
|
||||||
|
private int selectedSeason = -1;
|
||||||
|
private String selectedSeasonTitle = null;
|
||||||
private int selectedEpisodeId = -1;
|
private int selectedEpisodeId = -1;
|
||||||
|
|
||||||
private TVShowDetailsFragment tvshowDetailsFragment;
|
|
||||||
private boolean clearSharedElements;
|
private boolean clearSharedElements;
|
||||||
|
|
||||||
private NavigationDrawerFragment navigationDrawerFragment;
|
private NavigationDrawerFragment navigationDrawerFragment;
|
||||||
|
@ -108,6 +112,8 @@ public class TVShowsActivity extends BaseActivity
|
||||||
selectedTVShowId = savedInstanceState.getInt(TVSHOWID, -1);
|
selectedTVShowId = savedInstanceState.getInt(TVSHOWID, -1);
|
||||||
selectedTVShowTitle = savedInstanceState.getString(TVSHOWTITLE, null);
|
selectedTVShowTitle = savedInstanceState.getString(TVSHOWTITLE, null);
|
||||||
selectedEpisodeId = savedInstanceState.getInt(EPISODEID, -1);
|
selectedEpisodeId = savedInstanceState.getInt(EPISODEID, -1);
|
||||||
|
selectedSeason = savedInstanceState.getInt(SEASON, -1);
|
||||||
|
selectedSeasonTitle = savedInstanceState.getString(SEASONTITLE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupActionBar(selectedTVShowTitle);
|
setupActionBar(selectedTVShowTitle);
|
||||||
|
@ -124,6 +130,8 @@ public class TVShowsActivity extends BaseActivity
|
||||||
outState.putInt(TVSHOWID, selectedTVShowId);
|
outState.putInt(TVSHOWID, selectedTVShowId);
|
||||||
outState.putString(TVSHOWTITLE, selectedTVShowTitle);
|
outState.putString(TVSHOWTITLE, selectedTVShowTitle);
|
||||||
outState.putInt(EPISODEID, selectedEpisodeId);
|
outState.putInt(EPISODEID, selectedEpisodeId);
|
||||||
|
outState.putInt(SEASON, selectedSeason);
|
||||||
|
outState.putString(SEASONTITLE, selectedSeasonTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -152,6 +160,12 @@ public class TVShowsActivity extends BaseActivity
|
||||||
if (selectedEpisodeId != -1) {
|
if (selectedEpisodeId != -1) {
|
||||||
selectedEpisodeId = -1;
|
selectedEpisodeId = -1;
|
||||||
getSupportFragmentManager().popBackStack();
|
getSupportFragmentManager().popBackStack();
|
||||||
|
setupActionBar(selectedSeasonTitle);
|
||||||
|
return true;
|
||||||
|
} else if (selectedSeason != -1) {
|
||||||
|
selectedSeason = -1;
|
||||||
|
getSupportFragmentManager().popBackStack();
|
||||||
|
setupActionBar(selectedTVShowTitle);
|
||||||
return true;
|
return true;
|
||||||
} else if (selectedTVShowId != -1) {
|
} else if (selectedTVShowId != -1) {
|
||||||
selectedTVShowId = -1;
|
selectedTVShowId = -1;
|
||||||
|
@ -173,6 +187,10 @@ public class TVShowsActivity extends BaseActivity
|
||||||
// If we are showing episode or show details in portrait, clear selected and show action bar
|
// If we are showing episode or show details in portrait, clear selected and show action bar
|
||||||
if (selectedEpisodeId != -1) {
|
if (selectedEpisodeId != -1) {
|
||||||
selectedEpisodeId = -1;
|
selectedEpisodeId = -1;
|
||||||
|
setupActionBar(selectedSeasonTitle);
|
||||||
|
} else if (selectedSeason != -1) {
|
||||||
|
selectedSeason = -1;
|
||||||
|
setupActionBar(selectedTVShowTitle);
|
||||||
} else if (selectedTVShowId != -1) {
|
} else if (selectedTVShowId != -1) {
|
||||||
selectedTVShowId = -1;
|
selectedTVShowId = -1;
|
||||||
selectedTVShowTitle = null;
|
selectedTVShowTitle = null;
|
||||||
|
@ -182,19 +200,19 @@ public class TVShowsActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean drawerIndicatorIsArrow = false;
|
private boolean drawerIndicatorIsArrow = false;
|
||||||
private void setupActionBar(String tvshowTitle) {
|
private void setupActionBar(String title) {
|
||||||
Toolbar toolbar = (Toolbar)findViewById(R.id.default_toolbar);
|
Toolbar toolbar = (Toolbar)findViewById(R.id.default_toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar == null) return;
|
if (actionBar == null) return;
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
if (tvshowTitle != null) {
|
if (title != null) {
|
||||||
if (!drawerIndicatorIsArrow) {
|
if (!drawerIndicatorIsArrow) {
|
||||||
navigationDrawerFragment.animateDrawerToggle(true);
|
navigationDrawerFragment.animateDrawerToggle(true);
|
||||||
drawerIndicatorIsArrow = true;
|
drawerIndicatorIsArrow = true;
|
||||||
}
|
}
|
||||||
actionBar.setTitle(tvshowTitle);
|
actionBar.setTitle(title);
|
||||||
} else {
|
} else {
|
||||||
if (drawerIndicatorIsArrow) {
|
if (drawerIndicatorIsArrow) {
|
||||||
navigationDrawerFragment.animateDrawerToggle(false);
|
navigationDrawerFragment.animateDrawerToggle(false);
|
||||||
|
@ -207,7 +225,7 @@ public class TVShowsActivity extends BaseActivity
|
||||||
/**
|
/**
|
||||||
* Callback from tvshows list fragment when a show is selected.
|
* Callback from tvshows list fragment when a show is selected.
|
||||||
* Switch fragment in portrait
|
* Switch fragment in portrait
|
||||||
* @param vh
|
* @param vh view holder
|
||||||
*/
|
*/
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
public void onTVShowSelected(TVShowListFragment.ViewHolder vh) {
|
public void onTVShowSelected(TVShowListFragment.ViewHolder vh) {
|
||||||
|
@ -215,7 +233,7 @@ public class TVShowsActivity extends BaseActivity
|
||||||
selectedTVShowTitle = vh.tvshowTitle;
|
selectedTVShowTitle = vh.tvshowTitle;
|
||||||
|
|
||||||
// Replace list fragment
|
// Replace list fragment
|
||||||
tvshowDetailsFragment = TVShowDetailsFragment.newInstance(vh);
|
final TVShowDetailsFragment tvshowDetailsFragment = TVShowDetailsFragment.newInstance(vh);
|
||||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
// Set up transitions
|
// Set up transitions
|
||||||
|
@ -238,55 +256,71 @@ public class TVShowsActivity extends BaseActivity
|
||||||
};
|
};
|
||||||
tvshowDetailsFragment.setEnterSharedElementCallback(seCallback);
|
tvshowDetailsFragment.setEnterSharedElementCallback(seCallback);
|
||||||
|
|
||||||
tvshowDetailsFragment.setEnterTransition(TransitionInflater
|
tvshowDetailsFragment.setEnterTransition(
|
||||||
.from(this)
|
TransitionInflater.from(this)
|
||||||
.inflateTransition(R.transition.media_details));
|
.inflateTransition(R.transition.media_details));
|
||||||
tvshowDetailsFragment.setReturnTransition(null);
|
tvshowDetailsFragment.setReturnTransition(null);
|
||||||
Transition changeImageTransition = TransitionInflater.from(
|
Transition changeImageTransition =
|
||||||
this).inflateTransition(R.transition.change_image);
|
TransitionInflater.from(this)
|
||||||
|
.inflateTransition(R.transition.change_image);
|
||||||
tvshowDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
|
tvshowDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
|
||||||
tvshowDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
|
tvshowDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
|
||||||
fragTrans.addSharedElement(vh.artView, vh.artView.getTransitionName());
|
fragTrans.addSharedElement(vh.artView, vh.artView.getTransitionName());
|
||||||
} else {
|
} else {
|
||||||
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
|
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0, R.anim.fragment_list_popenter, 0);
|
||||||
R.anim.fragment_list_popenter, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragTrans.replace(R.id.fragment_container, tvshowDetailsFragment)
|
fragTrans.replace(R.id.fragment_container, tvshowDetailsFragment)
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit();
|
.commit();
|
||||||
setupActionBar(selectedTVShowTitle);
|
setupActionBar(selectedTVShowTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback from tvshow episodes list when a episode is selected
|
* Callback from tvshow details when a season is selected
|
||||||
* @param tvshowId Show id of the episode, should be the same as {@link #selectedTVShowId}
|
* @param tvshowId tv show id
|
||||||
* @param episodeId Episode id
|
* @param seasonId season number
|
||||||
*/
|
*/
|
||||||
@TargetApi(21)
|
public void onSeasonSelected(int tvshowId, int seasonId) {
|
||||||
public void onEpisodeSelected(int tvshowId, int episodeId) {
|
selectedSeason = seasonId;
|
||||||
selectedEpisodeId = episodeId;
|
|
||||||
|
// Replace fragment
|
||||||
|
TVShowEpisodeListFragment fragment =
|
||||||
|
TVShowEpisodeListFragment.newInstance(selectedTVShowId, seasonId);
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.setCustomAnimations(R.anim.fragment_details_enter, 0, R.anim.fragment_list_popenter, 0)
|
||||||
|
.replace(R.id.fragment_container, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
selectedSeasonTitle = String.format(getString(R.string.season_number), seasonId);
|
||||||
|
setupActionBar(selectedSeasonTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback from tvshow episodes list when a episode is selected
|
||||||
|
* @param vh view holder
|
||||||
|
*/
|
||||||
|
public void onEpisodeSelected(TVShowEpisodeListFragment.EpisodeViewHolder vh) {
|
||||||
|
selectedEpisodeId = vh.episodeId;
|
||||||
|
|
||||||
// Replace list fragment
|
// Replace list fragment
|
||||||
TVShowEpisodeDetailsFragment fragment =
|
TVShowEpisodeDetailsFragment fragment =
|
||||||
TVShowEpisodeDetailsFragment.newInstance(tvshowId, episodeId);
|
TVShowEpisodeDetailsFragment.newInstance(selectedTVShowId, selectedEpisodeId);
|
||||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
// Set up transitions
|
// Set up transitions
|
||||||
if (Utils.isLollipopOrLater()) {
|
if (Utils.isLollipopOrLater()) {
|
||||||
fragment.setEnterTransition(TransitionInflater
|
fragment.setEnterTransition(
|
||||||
.from(this)
|
TransitionInflater.from(this).inflateTransition(R.transition.media_details));
|
||||||
.inflateTransition(R.transition.media_details));
|
|
||||||
fragment.setReturnTransition(null);
|
fragment.setReturnTransition(null);
|
||||||
} else {
|
} else {
|
||||||
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
|
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0, R.anim.fragment_list_popenter, 0);
|
||||||
R.anim.fragment_list_popenter, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragTrans.replace(R.id.fragment_container, fragment)
|
fragTrans.replace(R.id.fragment_container, fragment)
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit();
|
.commit();
|
||||||
setupActionBar(selectedTVShowTitle);
|
setupActionBar(selectedTVShowTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,7 @@
|
||||||
style="@style/TextAppearance.Media.Info"
|
style="@style/TextAppearance.Media.Info"
|
||||||
android:paddingLeft="@dimen/default_padding"
|
android:paddingLeft="@dimen/default_padding"
|
||||||
android:paddingRight="@dimen/default_padding"
|
android:paddingRight="@dimen/default_padding"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
android:maxLines="@integer/description_max_lines"
|
android:maxLines="@integer/description_max_lines"
|
||||||
android:ellipsize="@null"/>
|
android:ellipsize="@null"/>
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
|
@ -84,26 +84,6 @@
|
||||||
android:contentDescription="@string/poster"
|
android:contentDescription="@string/poster"
|
||||||
android:scaleType="centerCrop"/>
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
<!-- Buttons-->
|
|
||||||
<!--<LinearLayout-->
|
|
||||||
<!--android:id="@+id/media_actions_bar"-->
|
|
||||||
<!--android:layout_width="match_parent"-->
|
|
||||||
<!--android:layout_height="@dimen/buttonbar_height"-->
|
|
||||||
<!--android:layout_below="@id/media_undertitle"-->
|
|
||||||
<!--android:paddingLeft="@dimen/default_padding"-->
|
|
||||||
<!--android:paddingRight="@dimen/default_padding"-->
|
|
||||||
<!--android:orientation="horizontal"-->
|
|
||||||
<!--style="@style/ButtonBar"-->
|
|
||||||
<!--android:background="?attr/contentBackgroundDimColor">-->
|
|
||||||
<!--<ImageButton-->
|
|
||||||
<!--android:id="@+id/go_to_imdb"-->
|
|
||||||
<!--android:layout_width="@dimen/buttonbar_button_width"-->
|
|
||||||
<!--android:layout_height="match_parent"-->
|
|
||||||
<!--style="@style/Widget.Button.Borderless"-->
|
|
||||||
<!--android:src="?attr/iconWWW"-->
|
|
||||||
<!--android:contentDescription="@string/imdb"/>-->
|
|
||||||
<!--</LinearLayout>-->
|
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/media_details"
|
android:id="@+id/media_details"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -156,37 +136,81 @@
|
||||||
android:text="@string/max_rating_video"/>
|
android:text="@string/max_rating_video"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/media_description"
|
android:id="@+id/media_description_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/media_details"
|
android:layout_below="@id/media_details"
|
||||||
style="@style/TextAppearance.Media.Info"
|
android:orientation="vertical"
|
||||||
android:paddingLeft="@dimen/default_padding"
|
android:background="?attr/contentBackgroundColor">
|
||||||
android:paddingRight="@dimen/default_padding"
|
<TextView
|
||||||
android:background="?attr/contentBackgroundColor"/>
|
android:id="@+id/media_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/TextAppearance.Media.Info"
|
||||||
|
android:paddingLeft="@dimen/default_padding"
|
||||||
|
android:paddingRight="@dimen/default_padding"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:maxLines="@integer/description_max_lines"
|
||||||
|
android:ellipsize="@null"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/show_all"
|
||||||
|
android:layout_width="@dimen/small_icon_size"
|
||||||
|
android:layout_height="@dimen/small_icon_size"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginRight="@dimen/small_padding"
|
||||||
|
android:layout_marginEnd="@dimen/small_padding"
|
||||||
|
android:src="?attr/iconExpand"
|
||||||
|
android:background="?android:attr/selectableItemBackground"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/separator"
|
android:id="@+id/separator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_below="@id/media_description"
|
android:layout_below="@id/media_description_container"
|
||||||
style="@style/DefaultDividerH"/>
|
style="@style/DefaultDividerH"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/cast_title"
|
android:id="@+id/seasons_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/separator"
|
android:layout_below="@id/separator"
|
||||||
style="@style/TextAppearance.Media.Title"
|
style="@style/TextAppearance.Media.Title"
|
||||||
android:paddingLeft="@dimen/default_padding"
|
android:paddingLeft="@dimen/default_padding"
|
||||||
android:paddingRight="@dimen/default_padding"
|
android:paddingRight="@dimen/default_padding"
|
||||||
|
android:paddingTop="@dimen/default_padding"
|
||||||
|
android:text="@string/tvshow_seasons"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TODO: Review the column count.
|
||||||
|
Currently set to 1 in all cases as any other value isn't working
|
||||||
|
-->
|
||||||
|
<GridLayout
|
||||||
|
android:id="@+id/seasons_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/seasons_title"
|
||||||
|
android:columnCount="@integer/seasons_grid_view_columns"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:useDefaultMargins="true"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cast_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/seasons_list"
|
||||||
|
style="@style/TextAppearance.Media.Title"
|
||||||
|
android:paddingLeft="@dimen/default_padding"
|
||||||
|
android:paddingRight="@dimen/default_padding"
|
||||||
|
android:paddingTop="@dimen/default_padding"
|
||||||
android:text="@string/cast"/>
|
android:text="@string/cast"/>
|
||||||
|
|
||||||
<GridLayout
|
<GridLayout
|
||||||
android:id="@+id/cast_list"
|
android:id="@+id/cast_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/cast_title"
|
android:layout_below="@id/cast_title"
|
||||||
android:columnCount="@integer/cast_grid_view_columns"
|
android:columnCount="@integer/cast_grid_view_columns"
|
||||||
android:orientation="horizontal"/>
|
android:orientation="horizontal"/>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015 Synced Synapse. All rights reserved.
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
card_view:cardElevation="@dimen/default_card_elevation"
|
||||||
|
card_view:cardBackgroundColor="?attr/appCardBackgroundColor"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/art"
|
||||||
|
android:layout_width="@dimen/seasonlist_art_width"
|
||||||
|
android:layout_height="@dimen/seasonlist_art_heigth"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:contentDescription="@string/poster"
|
||||||
|
android:scaleType="centerCrop"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/season"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@id/art"
|
||||||
|
android:layout_toEndOf="@id/art"
|
||||||
|
style="@style/TextAppearance.Medialist.Title"
|
||||||
|
android:paddingTop="@dimen/small_padding"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/episodes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignLeft="@id/season"
|
||||||
|
android:layout_alignStart="@id/season"
|
||||||
|
android:layout_below="@id/season"
|
||||||
|
style="@style/TextAppearance.Medialist.Details"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
</android.support.v7.widget.CardView>
|
|
@ -15,69 +15,89 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout
|
<android.support.v7.widget.CardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/container"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="@dimen/small_padding">
|
card_view:cardElevation="@dimen/default_card_elevation"
|
||||||
|
card_view:cardBackgroundColor="?attr/appCardBackgroundColor">
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_context_menu"
|
|
||||||
android:layout_width="@dimen/default_icon_size"
|
|
||||||
android:layout_height="@dimen/default_icon_size"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:padding="@dimen/default_icon_padding"
|
|
||||||
style="@style/Widget.Button.Borderless"
|
|
||||||
android:src="?attr/iconOverflow"
|
|
||||||
android:contentDescription="@string/action_options"/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/relativelayout_checkmark_epnumber"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="@dimen/seasonlist_art_width"
|
android:id="@+id/container"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_height="wrap_content">
|
||||||
android:layout_alignParentLeft="true">
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/art"
|
||||||
|
android:layout_width="@dimen/episodelist_art_width"
|
||||||
|
android:layout_height="@dimen/episodelist_art_heigth"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:contentDescription="@string/poster"
|
||||||
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/episode_number"
|
||||||
|
android:layout_width="@dimen/default_icon_size"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/TextAppearance.Medialist.Title"
|
||||||
|
android:paddingRight="@dimen/small_padding"
|
||||||
|
android:layout_alignTop="@id/art"
|
||||||
|
android:layout_toRightOf="@id/art"
|
||||||
|
android:layout_toEndOf="@id/art"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/checkmark"
|
android:id="@+id/checkmark"
|
||||||
android:layout_width="@dimen/default_icon_size"
|
android:layout_width="@dimen/default_icon_size"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/default_icon_size"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_below="@id/episode_number"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignLeft="@id/episode_number"
|
||||||
android:layout_marginTop="@dimen/small_padding"
|
android:layout_alignStart="@id/episode_number"
|
||||||
android:contentDescription="@string/seen"
|
android:padding="@dimen/default_icon_padding"
|
||||||
android:src="?attr/iconSeen"/>
|
android:src="?attr/iconSeen"
|
||||||
|
android:contentDescription="@string/seen"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/list_context_menu"
|
||||||
|
android:layout_width="@dimen/default_icon_size"
|
||||||
|
android:layout_height="@dimen/default_icon_size"
|
||||||
|
android:layout_alignTop="@id/art"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:padding="@dimen/default_icon_padding"
|
||||||
|
style="@style/Widget.Button.Borderless"
|
||||||
|
android:src="?attr/iconOverflow"
|
||||||
|
android:contentDescription="@string/action_options"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/episode_number"
|
android:id="@+id/title"
|
||||||
android:layout_width="@dimen/seasonlist_art_width"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignTop="@id/episode_number"
|
||||||
|
android:layout_toRightOf="@id/episode_number"
|
||||||
|
android:layout_toLeftOf="@id/list_context_menu"
|
||||||
|
android:layout_toEndOf="@id/episode_number"
|
||||||
|
android:layout_toStartOf="@id/list_context_menu"
|
||||||
style="@style/TextAppearance.Medialist.Title"
|
style="@style/TextAppearance.Medialist.Title"
|
||||||
android:gravity="end"/>
|
android:paddingTop="@dimen/small_padding"
|
||||||
|
android:paddingLeft="@dimen/small_padding"
|
||||||
|
android:paddingRight="@dimen/small_padding"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/default_icon_size"
|
||||||
|
android:layout_alignLeft="@id/title"
|
||||||
|
android:layout_alignStart="@id/title"
|
||||||
|
android:layout_below="@id/title"
|
||||||
|
style="@style/TextAppearance.Medialist.Details"
|
||||||
|
android:paddingLeft="@dimen/small_padding"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:gravity="center_vertical"/>
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignTop="@id/relativelayout_checkmark_epnumber"
|
|
||||||
android:layout_toRightOf="@id/relativelayout_checkmark_epnumber"
|
|
||||||
android:layout_toLeftOf="@id/list_context_menu"
|
|
||||||
android:layout_toEndOf="@id/relativelayout_checkmark_epnumber"
|
|
||||||
android:layout_toStartOf="@id/list_context_menu"
|
|
||||||
style="@style/TextAppearance.Medialist.Title"
|
|
||||||
android:paddingTop="@dimen/small_padding"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/details"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignLeft="@id/title"
|
|
||||||
android:layout_alignStart="@id/title"
|
|
||||||
android:layout_below="@id/title"
|
|
||||||
style="@style/TextAppearance.Medialist.Details"
|
|
||||||
android:paddingBottom="@dimen/small_padding"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
|
@ -1,56 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2015 Synced Synapse. All rights reserved.
|
|
||||||
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/art"
|
|
||||||
android:layout_width="@dimen/seasonlist_art_width"
|
|
||||||
android:layout_height="@dimen/seasonlist_art_heigth"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:contentDescription="@string/poster"
|
|
||||||
android:scaleType="centerCrop"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/season"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_toRightOf="@id/art"
|
|
||||||
android:layout_toEndOf="@id/art"
|
|
||||||
style="@style/TextAppearance.Medialist.Title"
|
|
||||||
android:paddingTop="@dimen/small_padding"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/episodes"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignLeft="@id/season"
|
|
||||||
android:layout_alignStart="@id/season"
|
|
||||||
android:layout_below="@id/season"
|
|
||||||
style="@style/TextAppearance.Medialist.Details"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/status_indicator"
|
|
||||||
android:layout_width="@dimen/default_icon_size"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_marginTop="@dimen/small_padding"/>
|
|
||||||
</RelativeLayout>
|
|
|
@ -17,4 +17,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<integer name="cast_grid_view_columns">5</integer>
|
<integer name="cast_grid_view_columns">5</integer>
|
||||||
<integer name="cast_grid_view_rows">2</integer>
|
<integer name="cast_grid_view_rows">2</integer>
|
||||||
|
|
||||||
|
<integer name="seasons_grid_view_columns">1</integer>
|
||||||
</resources>
|
</resources>
|
|
@ -17,4 +17,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<integer name="cast_grid_view_columns">6</integer>
|
<integer name="cast_grid_view_columns">6</integer>
|
||||||
<integer name="cast_grid_view_rows">2</integer>
|
<integer name="cast_grid_view_rows">2</integer>
|
||||||
|
|
||||||
|
<integer name="seasons_grid_view_columns">1</integer>
|
||||||
</resources>
|
</resources>
|
|
@ -45,8 +45,8 @@
|
||||||
<dimen name="seasonlist_art_width">86dp</dimen>
|
<dimen name="seasonlist_art_width">86dp</dimen>
|
||||||
<dimen name="seasonlist_art_heigth">124dp</dimen>
|
<dimen name="seasonlist_art_heigth">124dp</dimen>
|
||||||
|
|
||||||
<dimen name="episodelist_art_width">134dp</dimen>
|
<dimen name="episodelist_art_width">112dp</dimen>
|
||||||
<dimen name="episodelist_art_heigth">76dp</dimen>
|
<dimen name="episodelist_art_heigth">100dp</dimen>
|
||||||
|
|
||||||
<dimen name="artistlist_art_width">88dp</dimen>
|
<dimen name="artistlist_art_width">88dp</dimen>
|
||||||
<dimen name="artistlist_art_heigth">88dp</dimen>
|
<dimen name="artistlist_art_heigth">88dp</dimen>
|
||||||
|
|
|
@ -16,5 +16,7 @@
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<integer name="cast_grid_view_columns">4</integer>
|
<integer name="cast_grid_view_columns">4</integer>
|
||||||
<integer name="cast_grid_view_rows">3</integer>
|
<integer name="cast_grid_view_rows">3</integer>
|
||||||
|
|
||||||
|
<integer name="seasons_grid_view_columns">1</integer>
|
||||||
</resources>
|
</resources>
|
|
@ -86,8 +86,8 @@
|
||||||
<dimen name="seasonlist_art_width">72dp</dimen>
|
<dimen name="seasonlist_art_width">72dp</dimen>
|
||||||
<dimen name="seasonlist_art_heigth">104dp</dimen>
|
<dimen name="seasonlist_art_heigth">104dp</dimen>
|
||||||
|
|
||||||
<dimen name="episodelist_art_width">112dp</dimen>
|
<dimen name="episodelist_art_width">84dp</dimen>
|
||||||
<dimen name="episodelist_art_heigth">64dp</dimen>
|
<dimen name="episodelist_art_heigth">84dp</dimen>
|
||||||
|
|
||||||
<dimen name="artistlist_art_width">74dp</dimen>
|
<dimen name="artistlist_art_width">74dp</dimen>
|
||||||
<dimen name="artistlist_art_heigth">74dp</dimen>
|
<dimen name="artistlist_art_heigth">74dp</dimen>
|
||||||
|
|
|
@ -25,5 +25,7 @@
|
||||||
<integer name="cast_grid_view_rows">3</integer>
|
<integer name="cast_grid_view_rows">3</integer>
|
||||||
<integer name="description_max_lines">3</integer>
|
<integer name="description_max_lines">3</integer>
|
||||||
|
|
||||||
|
<integer name="seasons_grid_view_columns">1</integer>
|
||||||
|
|
||||||
<integer name="fab_animation_start_delay">100</integer>
|
<integer name="fab_animation_start_delay">100</integer>
|
||||||
</resources>
|
</resources>
|
|
@ -287,6 +287,7 @@
|
||||||
|
|
||||||
<string name="tvshow_overview">Overview</string>
|
<string name="tvshow_overview">Overview</string>
|
||||||
<string name="tvshow_episodes">Episodes</string>
|
<string name="tvshow_episodes">Episodes</string>
|
||||||
|
<string name="tvshow_seasons">Seasons</string>
|
||||||
|
|
||||||
<string name="addon_overview">Overview</string>
|
<string name="addon_overview">Overview</string>
|
||||||
<string name="addon_content">Content</string>
|
<string name="addon_content">Content</string>
|
||||||
|
|
Loading…
Reference in New Issue