Merge pull request #138 from poisdeux/sharedelementtransitions
Implemented shared element transition for movie posters
This commit is contained in:
commit
abd1b2fc67
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Resources;
|
||||
|
@ -44,6 +45,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.melnykov.fab.FloatingActionButton;
|
||||
import com.melnykov.fab.ObservableScrollView;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.Settings;
|
||||
import org.xbmc.kore.jsonrpc.ApiCallback;
|
||||
|
@ -74,8 +76,14 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
private static final String TAG = LogUtils.makeLogTag(MovieDetailsFragment.class);
|
||||
|
||||
public static final String MOVIEID = "movie_id";
|
||||
|
||||
public static final String BUNDLE_KEY_MOVIETITLE = "movie_title";
|
||||
public static final String BUNDLE_KEY_MOVIEPLOT = "movie_plot";
|
||||
public static final String BUNDLE_KEY_MOVIEID = "movie_id";
|
||||
public static final String POSTER_TRANS_NAME = "POSTER_TRANS_NAME";
|
||||
public static final String BUNDLE_KEY_MOVIEGENRES = "movie_genres";
|
||||
public static final String BUNDLE_KEY_MOVIEYEAR = "movie_year";
|
||||
public static final String BUNDLE_KEY_MOVIERUNTIME = "movie_runtime";
|
||||
public static final String BUNDLE_KEY_MOVIERATING = "movie_rating";
|
||||
// Loader IDs
|
||||
private static final int LOADER_MOVIE = 0,
|
||||
LOADER_CAST = 1;
|
||||
|
@ -129,18 +137,31 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
/**
|
||||
* Create a new instance of this, initialized to show the movie movieId
|
||||
*/
|
||||
public static MovieDetailsFragment newInstance(int movieId) {
|
||||
@TargetApi(21)
|
||||
public static MovieDetailsFragment newInstance(MovieListFragment.ViewHolder vh) {
|
||||
MovieDetailsFragment fragment = new MovieDetailsFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(MOVIEID, movieId);
|
||||
args.putInt(BUNDLE_KEY_MOVIEID, vh.movieId);
|
||||
args.putString(BUNDLE_KEY_MOVIETITLE, vh.movieTitle);
|
||||
args.putString(BUNDLE_KEY_MOVIEPLOT, vh.movieTagline);
|
||||
args.putString(BUNDLE_KEY_MOVIEGENRES, vh.movieGenres);
|
||||
args.putInt(BUNDLE_KEY_MOVIEYEAR, vh.movieYear);
|
||||
args.putInt(BUNDLE_KEY_MOVIERUNTIME, vh.movieRuntime);
|
||||
args.putDouble(BUNDLE_KEY_MOVIERATING, vh.movieRating);
|
||||
if( Utils.isLollipopOrLater()) {
|
||||
args.putString(POSTER_TRANS_NAME, vh.artView.getTransitionName());
|
||||
}
|
||||
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
@Override
|
||||
protected View createView(LayoutInflater inflater, ViewGroup container) {
|
||||
movieId = getArguments().getInt(MOVIEID, -1);
|
||||
Bundle bundle = getArguments();
|
||||
movieId = bundle.getInt(BUNDLE_KEY_MOVIEID, -1);
|
||||
|
||||
if (movieId == -1) {
|
||||
// There's nothing to show
|
||||
|
@ -167,10 +188,20 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
FloatingActionButton fab = (FloatingActionButton)fabButton;
|
||||
fab.attachToScrollView((ObservableScrollView) mediaPanel);
|
||||
|
||||
if(Utils.isLollipopOrLater()) {
|
||||
mediaPoster.setTransitionName(getArguments().getString(POSTER_TRANS_NAME));
|
||||
}
|
||||
|
||||
mediaTitle.setText(bundle.getString(BUNDLE_KEY_MOVIETITLE));
|
||||
mediaUndertitle.setText(bundle.getString(BUNDLE_KEY_MOVIEPLOT));
|
||||
mediaGenres.setText(bundle.getString(BUNDLE_KEY_MOVIEGENRES));
|
||||
setMediaYear(bundle.getInt(BUNDLE_KEY_MOVIERUNTIME), bundle.getInt(BUNDLE_KEY_MOVIEYEAR));
|
||||
setMediaRating(bundle.getDouble(BUNDLE_KEY_MOVIERATING));
|
||||
|
||||
// Pad main content view to overlap with bottom system bar
|
||||
// UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true);
|
||||
// mediaPanel.setClipToPadding(false);
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
@ -209,9 +240,18 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
public void onResume() {
|
||||
// Force the exit view to invisible
|
||||
exitTransitionView.setVisibility(View.INVISIBLE);
|
||||
//As we make mediaPoster invisible in onStop() we need to make it visible here.
|
||||
mediaPoster.setVisibility(View.VISIBLE);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
//For some reason poster is included in the bottom slide animation, by making it invisible it is not noticeable for the user
|
||||
mediaPoster.setVisibility(View.INVISIBLE);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSyncProcessEnded(MediaSyncEvent event) {
|
||||
if (event.status == MediaSyncEvent.STATUS_SUCCESS) {
|
||||
|
@ -456,12 +496,8 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
mediaTitle.setText(movieTitle);
|
||||
mediaUndertitle.setText(cursor.getString(MovieDetailsQuery.TAGLINE));
|
||||
|
||||
int runtime = cursor.getInt(MovieDetailsQuery.RUNTIME) / 60;
|
||||
String durationYear = runtime > 0 ?
|
||||
String.format(getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
|
||||
" | " + String.valueOf(cursor.getInt(MovieDetailsQuery.YEAR)) :
|
||||
String.valueOf(cursor.getInt(MovieDetailsQuery.YEAR));
|
||||
mediaYear.setText(durationYear);
|
||||
setMediaYear(cursor.getInt(MovieDetailsQuery.RUNTIME) / 60, cursor.getInt(MovieDetailsQuery.YEAR));
|
||||
|
||||
mediaGenres.setText(cursor.getString(MovieDetailsQuery.GENRES));
|
||||
|
||||
double rating = cursor.getDouble(MovieDetailsQuery.RATING);
|
||||
|
@ -469,8 +505,7 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
mediaRating.setVisibility(View.VISIBLE);
|
||||
mediaMaxRating.setVisibility(View.VISIBLE);
|
||||
mediaRatingVotes.setVisibility(View.VISIBLE);
|
||||
mediaRating.setText(String.format("%01.01f", rating));
|
||||
mediaMaxRating.setText(getString(R.string.max_rating_video));
|
||||
setMediaRating(rating);
|
||||
String votes = cursor.getString(MovieDetailsQuery.VOTES);
|
||||
mediaRatingVotes.setText((TextUtils.isEmpty(votes)) ?
|
||||
"" : String.format(getString(R.string.votes), votes));
|
||||
|
@ -508,7 +543,6 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
movieTitle, cursor.getString(MovieDetailsQuery.FILE));
|
||||
|
||||
// Check if downloaded file exists
|
||||
downloadButton.setVisibility(View.VISIBLE);
|
||||
if (movieDownloadInfo.downloadFileExists()) {
|
||||
Resources.Theme theme = getActivity().getTheme();
|
||||
TypedArray styledAttributes = theme.obtainStyledAttributes(new int[]{
|
||||
|
@ -522,6 +556,19 @@ public class MovieDetailsFragment extends AbstractDetailsFragment
|
|||
}
|
||||
}
|
||||
|
||||
private void setMediaRating(double rating) {
|
||||
mediaRating.setText(String.format("%01.01f", rating));
|
||||
mediaMaxRating.setText(getString(R.string.max_rating_video));
|
||||
}
|
||||
|
||||
private void setMediaYear(int runtime, int year) {
|
||||
String durationYear = runtime > 0 ?
|
||||
String.format(getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
|
||||
" | " + year :
|
||||
String.valueOf(year);
|
||||
mediaYear.setText(durationYear);
|
||||
}
|
||||
|
||||
private void setupSeenButton(int playcount) {
|
||||
// Seen button
|
||||
if (playcount > 0) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.xbmc.kore.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -47,6 +48,7 @@ import org.xbmc.kore.provider.MediaDatabase;
|
|||
import org.xbmc.kore.service.LibrarySyncService;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
import org.xbmc.kore.utils.Utils;
|
||||
|
||||
/**
|
||||
* Fragment that presents the movie list
|
||||
|
@ -55,7 +57,7 @@ public class MovieListFragment extends AbstractListFragment {
|
|||
private static final String TAG = LogUtils.makeLogTag(MovieListFragment.class);
|
||||
|
||||
public interface OnMovieSelectedListener {
|
||||
public void onMovieSelected(int movieId, String movieTitle);
|
||||
public void onMovieSelected(ViewHolder vh);
|
||||
}
|
||||
|
||||
// Activity listener
|
||||
|
@ -72,7 +74,7 @@ public class MovieListFragment extends AbstractListFragment {
|
|||
// Get the movie id from the tag
|
||||
ViewHolder tag = (ViewHolder) view.getTag();
|
||||
// Notify the activity
|
||||
listenerActivity.onMovieSelected(tag.movieId, tag.movieTitle);
|
||||
listenerActivity.onMovieSelected(tag);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -253,16 +255,16 @@ public class MovieListFragment extends AbstractListFragment {
|
|||
// the user transitions to that fragment, avoiding another call and imediatelly showing the image
|
||||
Resources resources = context.getResources();
|
||||
artWidth = (int)(resources.getDimension(R.dimen.now_playing_poster_width) /
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
artHeight = (int)(resources.getDimension(R.dimen.now_playing_poster_height) /
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
UIUtils.IMAGE_RESIZE_FACTOR);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public View newView(Context context, final Cursor cursor, ViewGroup parent) {
|
||||
final View view = LayoutInflater.from(context)
|
||||
.inflate(R.layout.grid_item_movie, parent, false);
|
||||
.inflate(R.layout.grid_item_movie, parent, false);
|
||||
|
||||
// Setup View holder pattern
|
||||
ViewHolder viewHolder = new ViewHolder();
|
||||
|
@ -277,6 +279,7 @@ public class MovieListFragment extends AbstractListFragment {
|
|||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@TargetApi(21)
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final ViewHolder viewHolder = (ViewHolder)view.getTag();
|
||||
|
@ -284,36 +287,50 @@ public class MovieListFragment extends AbstractListFragment {
|
|||
// Save the movie id
|
||||
viewHolder.movieId = cursor.getInt(MovieListQuery.MOVIEID);
|
||||
viewHolder.movieTitle = cursor.getString(MovieListQuery.TITLE);
|
||||
viewHolder.movieTagline = cursor.getString(MovieListQuery.TAGLINE);
|
||||
viewHolder.movieYear = cursor.getInt(MovieListQuery.YEAR);
|
||||
viewHolder.movieRating = cursor.getDouble(MovieListQuery.RATING);
|
||||
|
||||
viewHolder.titleView.setText(viewHolder.movieTitle);
|
||||
String details = TextUtils.isEmpty(cursor.getString(MovieListQuery.TAGLINE)) ?
|
||||
cursor.getString(MovieListQuery.GENRES) :
|
||||
cursor.getString(MovieListQuery.TAGLINE);
|
||||
|
||||
viewHolder.movieGenres = cursor.getString(MovieListQuery.GENRES);
|
||||
String details = TextUtils.isEmpty(viewHolder.movieTagline) ?
|
||||
viewHolder.movieGenres :
|
||||
viewHolder.movieTagline;
|
||||
viewHolder.detailsView.setText(details);
|
||||
// viewHolder.yearView.setText(String.valueOf(cursor.getInt(MovieListQuery.YEAR)));
|
||||
int runtime = cursor.getInt(MovieListQuery.RUNTIME) / 60;
|
||||
String duration = runtime > 0 ?
|
||||
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(runtime)) +
|
||||
" | " + String.valueOf(cursor.getInt(MovieListQuery.YEAR)) :
|
||||
String.valueOf(cursor.getInt(MovieListQuery.YEAR));
|
||||
viewHolder.movieRuntime = cursor.getInt(MovieListQuery.RUNTIME) / 60;
|
||||
String duration = viewHolder.movieRuntime > 0 ?
|
||||
String.format(context.getString(R.string.minutes_abbrev), String.valueOf(viewHolder.movieRuntime)) +
|
||||
" | " + viewHolder.movieYear :
|
||||
String.valueOf(viewHolder.movieYear);
|
||||
viewHolder.durationView.setText(duration);
|
||||
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
|
||||
cursor.getString(MovieListQuery.THUMBNAIL), viewHolder.movieTitle,
|
||||
viewHolder.artView, artWidth, artHeight);
|
||||
|
||||
if(Utils.isLollipopOrLater()) {
|
||||
viewHolder.artView.setTransitionName("a"+viewHolder.movieId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* View holder pattern
|
||||
*/
|
||||
private static class ViewHolder {
|
||||
public static class ViewHolder {
|
||||
TextView titleView;
|
||||
TextView detailsView;
|
||||
// TextView yearView;
|
||||
// TextView yearView;
|
||||
TextView durationView;
|
||||
ImageView artView;
|
||||
|
||||
int movieId;
|
||||
String movieTitle;
|
||||
String movieTagline;
|
||||
int movieYear;
|
||||
int movieRuntime;
|
||||
String movieGenres;
|
||||
double movieRating;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.support.v4.app.FragmentTransaction;
|
|||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
@ -67,10 +68,14 @@ public class MoviesActivity extends BaseActivity
|
|||
|
||||
// Setup animations
|
||||
if (Utils.isLollipopOrLater()) {
|
||||
movieListFragment.setExitTransition(null);
|
||||
movieListFragment.setReenterTransition(TransitionInflater
|
||||
//Fade added to prevent shared element from disappearing very shortly at the start of the transition.
|
||||
Transition fade = TransitionInflater
|
||||
.from(this)
|
||||
.inflateTransition(android.R.transition.fade));
|
||||
.inflateTransition(android.R.transition.fade);
|
||||
movieListFragment.setExitTransition(fade);
|
||||
movieListFragment.setReenterTransition(fade);
|
||||
movieListFragment.setSharedElementReturnTransition(TransitionInflater.from(
|
||||
this).inflateTransition(R.transition.change_image));
|
||||
}
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
|
@ -168,31 +173,43 @@ public class MoviesActivity extends BaseActivity
|
|||
/**
|
||||
* Callback from movielist fragment when a movie is selected.
|
||||
* Switch fragment in portrait
|
||||
* @param movieId Movie selected
|
||||
* @param movieTitle Title
|
||||
* @param vh ViewHolder holding movie info of item clicked
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public void onMovieSelected(int movieId, String movieTitle) {
|
||||
selectedMovieId = movieId;
|
||||
selectedMovieTitle = movieTitle;
|
||||
|
||||
MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(movieId);
|
||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||
public void onMovieSelected(MovieListFragment.ViewHolder vh) {
|
||||
selectedMovieTitle = vh.movieTitle;
|
||||
selectedMovieId = vh.movieId;
|
||||
|
||||
// Set up transitions
|
||||
if (Utils.isLollipopOrLater()) {
|
||||
MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh);
|
||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||
|
||||
movieDetailsFragment.setEnterTransition(TransitionInflater
|
||||
.from(this)
|
||||
.inflateTransition(R.transition.media_details));
|
||||
movieDetailsFragment.setReturnTransition(null);
|
||||
|
||||
Transition changeImageTransition = TransitionInflater.from(
|
||||
this).inflateTransition(R.transition.change_image);
|
||||
movieDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
|
||||
movieDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
|
||||
|
||||
fragTrans.replace(R.id.fragment_container, movieDetailsFragment)
|
||||
.addToBackStack(null)
|
||||
.addSharedElement(vh.artView, vh.artView.getTransitionName())
|
||||
.commit();
|
||||
} else {
|
||||
MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh);
|
||||
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
|
||||
|
||||
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconDownload"
|
||||
android:contentDescription="@string/download"
|
||||
android:visibility="gone"/>
|
||||
/>
|
||||
<ImageButton
|
||||
android:id="@+id/seen"
|
||||
android:layout_width="@dimen/buttonbar_button_width"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<changeTransform />
|
||||
<changeBounds />
|
||||
</transitionSet>
|
|
@ -19,6 +19,7 @@
|
|||
<fade>
|
||||
<targets>
|
||||
<target android:excludeId="@id/media_panel_group"/>
|
||||
<target android:excludeId="@id/fab"/>
|
||||
</targets>
|
||||
</fade>
|
||||
<!--<slide android:slideEdge="top">-->
|
||||
|
|
Loading…
Reference in New Issue