Merge pull request #143 from poisdeux/sharedelementtransitions

Implemented shared element transitions for albums
This commit is contained in:
Synced Synapse 2015-12-09 18:53:05 +00:00
commit f0cea00730
4 changed files with 159 additions and 104 deletions

View File

@ -16,13 +16,15 @@
package org.xbmc.kore.ui;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.net.Uri;
import android.os.*;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.BaseColumns;
import android.support.v4.app.Fragment;
@ -46,6 +48,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.host.HostInfo;
@ -59,6 +62,7 @@ import org.xbmc.kore.provider.MediaContract;
import org.xbmc.kore.utils.FileDownloadHelper;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
import java.io.File;
import java.util.ArrayList;
@ -67,7 +71,6 @@ import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import de.greenrobot.event.EventBus;
/**
* Presents movie details
@ -76,7 +79,13 @@ public class AlbumDetailsFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = LogUtils.makeLogTag(AlbumDetailsFragment.class);
public static final String ALBUMID = "album_id";
public static final String BUNDLE_KEY_ALBUMID = "album_id";
public static final String POSTER_TRANS_NAME = "POSTER_TRANS_NAME";
public static final String BUNDLE_KEY_ALBUMARTIST = "album_artist";
public static final String BUNDLE_KEY_ALBUMTITLE = "album_title";
public static final String BUNDLE_KEY_ALBUMGENRE = "album_genre";
public static final String BUNDLE_KEY_ALBUMYEAR = "album_year";
public static final String BUNDLE_KEY_ALBUMRATING = "album_rating";
// Loader IDs
private static final int LOADER_ALBUM = 0,
@ -84,7 +93,6 @@ public class AlbumDetailsFragment extends Fragment
private HostManager hostManager;
private HostInfo hostInfo;
private EventBus bus;
/**
* Handler on which to post RPC callbacks
@ -128,11 +136,21 @@ public class AlbumDetailsFragment extends Fragment
/**
* Create a new instance of this, initialized to show the album albumId
*/
public static AlbumDetailsFragment newInstance(int albumId) {
@TargetApi(21)
public static AlbumDetailsFragment newInstance(AlbumListFragment.ViewHolder vh) {
AlbumDetailsFragment fragment = new AlbumDetailsFragment();
Bundle args = new Bundle();
args.putInt(ALBUMID, albumId);
args.putInt(BUNDLE_KEY_ALBUMID, vh.albumId);
args.putString(BUNDLE_KEY_ALBUMTITLE, vh.albumTitle);
args.putString(BUNDLE_KEY_ALBUMARTIST, vh.albumArtist);
args.putString(BUNDLE_KEY_ALBUMGENRE, vh.albumGenre);
args.putInt(BUNDLE_KEY_ALBUMYEAR, vh.albumYear);
args.putDouble(BUNDLE_KEY_ALBUMRATING, vh.albumRating);
if( Utils.isLollipopOrLater()) {
args.putString(POSTER_TRANS_NAME, vh.artView.getTransitionName());
}
fragment.setArguments(args);
return fragment;
}
@ -143,8 +161,9 @@ public class AlbumDetailsFragment extends Fragment
}
@Override
@TargetApi(21)
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
albumId = getArguments().getInt(ALBUMID, -1);
albumId = getArguments().getInt(BUNDLE_KEY_ALBUMID, -1);
if ((container == null) || (albumId == -1)) {
// We're not being shown or there's nothing to show
@ -154,7 +173,6 @@ public class AlbumDetailsFragment extends Fragment
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_album_details, container, false);
ButterKnife.inject(this, root);
bus = EventBus.getDefault();
hostManager = HostManager.getInstance(getActivity());
hostInfo = hostManager.getHostInfo();
@ -173,6 +191,17 @@ public class AlbumDetailsFragment extends Fragment
FloatingActionButton fab = (FloatingActionButton)fabButton;
fab.attachToScrollView((ObservableScrollView) mediaPanel);
Bundle bundle = getArguments();
if(Utils.isLollipopOrLater()) {
mediaPoster.setTransitionName(bundle.getString(POSTER_TRANS_NAME));
}
mediaTitle.setText(bundle.getString(BUNDLE_KEY_ALBUMTITLE));
mediaUndertitle.setText(bundle.getString(BUNDLE_KEY_ALBUMARTIST));
setMediaYear(bundle.getString(BUNDLE_KEY_ALBUMGENRE), bundle.getInt(BUNDLE_KEY_ALBUMYEAR));
setMediaRating(bundle.getDouble(BUNDLE_KEY_ALBUMRATING));
// Pad main content view to overlap with bottom system bar
// UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true);
// mediaPanel.setClipToPadding(false);
@ -197,11 +226,6 @@ public class AlbumDetailsFragment extends Fragment
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@ -281,7 +305,7 @@ public class AlbumDetailsFragment extends Fragment
if (!isAdded()) return;
// Got an error, show toast
Toast.makeText(getActivity(), R.string.unable_to_connect_to_xbmc, Toast.LENGTH_SHORT)
.show();
.show();
}
}, callbackHandler);
}
@ -294,7 +318,7 @@ public class AlbumDetailsFragment extends Fragment
@OnClick(R.id.download)
public void onDownloadClicked(View v) {
if ((albumTitle == null) || (albumDisplayArtist == null) ||
(songInfoList == null) || (songInfoList.size() == 0)) {
(songInfoList == null) || (songInfoList.size() == 0)) {
// Nothing to download
Toast.makeText(getActivity(), R.string.no_files_to_download, Toast.LENGTH_SHORT).show();
return;
@ -311,16 +335,16 @@ public class AlbumDetailsFragment extends Fragment
if (file.exists()) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.download)
.setMessage(R.string.download_dir_exists)
.setPositiveButton(R.string.overwrite,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfoList, FileDownloadHelper.OVERWRITE_FILES,
callbackHandler);
}
})
.setMessage(R.string.download_dir_exists)
.setPositiveButton(R.string.overwrite,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfoList, FileDownloadHelper.OVERWRITE_FILES,
callbackHandler);
}
})
.setNeutralButton(R.string.download_with_new_name,
new DialogInterface.OnClickListener() {
@Override
@ -365,21 +389,13 @@ public class AlbumDetailsFragment extends Fragment
mediaTitle.setText(albumTitle);
mediaUndertitle.setText(albumDisplayArtist);
int year = cursor.getInt(AlbumDetailsQuery.YEAR);
String genres = cursor.getString(AlbumDetailsQuery.GENRE);
String label = (year > 0) ?
(!TextUtils.isEmpty(genres) ?
genres + " | " + String.valueOf(year) :
String.valueOf(year)) :
genres;
mediaYear.setText(label);
setMediaYear(cursor.getString(AlbumDetailsQuery.GENRE), cursor.getInt(AlbumDetailsQuery.YEAR));
double rating = cursor.getDouble(AlbumDetailsQuery.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_music));
setMediaRating(rating);
} else {
mediaRating.setVisibility(View.GONE);
mediaMaxRating.setVisibility(View.GONE);
@ -427,32 +443,35 @@ public class AlbumDetailsFragment extends Fragment
int artHeight = resources.getDimensionPixelOffset(R.dimen.now_playing_art_height),
artWidth = displayMetrics.widthPixels;
int posterWidth = resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_width);
int posterHeight = resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_heigth);
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
poster, albumTitle,
mediaPoster, posterWidth, posterHeight);
if (!TextUtils.isEmpty(fanart)) {
int posterWidth = resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_width);
int posterHeight = resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_heigth);
mediaPoster.setVisibility(View.VISIBLE);
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
poster, albumTitle,
mediaPoster, posterWidth, posterHeight);
UIUtils.loadImageIntoImageview(hostManager,
fanart,
mediaArt, artWidth, artHeight);
} else {
// No fanart, just present the poster
mediaPoster.setVisibility(View.GONE);
UIUtils.loadImageIntoImageview(hostManager,
poster,
mediaArt, artWidth, artHeight);
// Reset padding
int paddingLeft = mediaTitle.getPaddingRight(),
paddingRight = mediaTitle.getPaddingRight(),
paddingTop = mediaTitle.getPaddingTop(),
paddingBottom = mediaTitle.getPaddingBottom();
mediaTitle.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
mediaUndertitle.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
UIUtils.loadImageWithCharacterAvatar(getActivity(), hostManager,
poster, albumTitle, mediaArt, artWidth, artHeight);
}
}
private void setMediaRating(double rating) {
mediaRating.setText(String.format("%01.01f", rating));
mediaMaxRating.setText(getString(R.string.max_rating_music));
}
private void setMediaYear(String genres, int year) {
String label = (year > 0) ?
(!TextUtils.isEmpty(genres) ?
genres + " | " + String.valueOf(year) :
String.valueOf(year)) :
genres;
mediaYear.setText(label);
}
/**
* Starts playing the song on XBMC
* @param songId song to play
@ -501,7 +520,7 @@ public class AlbumDetailsFragment extends Fragment
if (!isAdded()) return;
// Got an error, show toast
Toast.makeText(getActivity(), R.string.item_added_to_playlist, Toast.LENGTH_SHORT)
.show();
.show();
}
@Override
@ -509,12 +528,12 @@ public class AlbumDetailsFragment extends Fragment
if (!isAdded()) return;
// Got an error, show toast
Toast.makeText(getActivity(), R.string.unable_to_connect_to_xbmc, Toast.LENGTH_SHORT)
.show();
.show();
}
}, callbackHandler);
} else {
Toast.makeText(getActivity(), R.string.no_suitable_playlist, Toast.LENGTH_SHORT)
.show();
.show();
}
}
@ -523,7 +542,7 @@ public class AlbumDetailsFragment extends Fragment
if (!isAdded()) return;
// Got an error, show toast
Toast.makeText(getActivity(), R.string.unable_to_connect_to_xbmc, Toast.LENGTH_SHORT)
.show();
.show();
}
}, callbackHandler);
}
@ -559,31 +578,31 @@ public class AlbumDetailsFragment extends Fragment
if (file.exists()) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.download)
.setMessage(R.string.download_file_exists)
.setPositiveButton(R.string.overwrite,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfo, FileDownloadHelper.OVERWRITE_FILES,
callbackHandler);
}
})
.setNeutralButton(R.string.download_with_new_name,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfo, FileDownloadHelper.DOWNLOAD_WITH_NEW_NAME,
callbackHandler);
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { }
})
.show();
.setMessage(R.string.download_file_exists)
.setPositiveButton(R.string.overwrite,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfo, FileDownloadHelper.OVERWRITE_FILES,
callbackHandler);
}
})
.setNeutralButton(R.string.download_with_new_name,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfo, FileDownloadHelper.DOWNLOAD_WITH_NEW_NAME,
callbackHandler);
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { }
})
.show();
} else {
FileDownloadHelper.downloadFiles(getActivity(), hostInfo,
songInfo, FileDownloadHelper.DOWNLOAD_WITH_NEW_NAME,
@ -608,7 +627,7 @@ public class AlbumDetailsFragment extends Fragment
songInfoList = new ArrayList<FileDownloadHelper.SongInfo>(cursor.getCount());
do {
View songView = LayoutInflater.from(getActivity())
.inflate(R.layout.list_item_song, songListView, false);
.inflate(R.layout.list_item_song, songListView, false);
TextView songTitle = (TextView)songView.findViewById(R.id.song_title);
TextView trackNumber = (TextView)songView.findViewById(R.id.track_number);
TextView duration = (TextView)songView.findViewById(R.id.duration);
@ -637,7 +656,6 @@ public class AlbumDetailsFragment extends Fragment
} while (cursor.moveToNext());
if (songInfoList.size() > 0) {
downloadButton.setVisibility(View.VISIBLE);
// Check if download dir exists
FileDownloadHelper.SongInfo songInfo = new FileDownloadHelper.SongInfo
(albumDisplayArtist, albumTitle, 0, 0, null, null);

View File

@ -15,6 +15,7 @@
*/
package org.xbmc.kore.ui;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@ -48,6 +49,7 @@ import org.xbmc.kore.service.LibrarySyncService;
import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils;
/**
* Fragment that presents the albums list
@ -56,7 +58,7 @@ public class AlbumListFragment extends AbstractListFragment {
private static final String TAG = LogUtils.makeLogTag(AlbumListFragment.class);
public interface OnAlbumSelectedListener {
public void onAlbumSelected(int albumId, String albumTitle);
public void onAlbumSelected(ViewHolder vh);
}
private static final String GENREID = "genreid",
@ -103,7 +105,7 @@ public class AlbumListFragment extends AbstractListFragment {
// Get the movie id from the tag
ViewHolder tag = (ViewHolder) view.getTag();
// Notify the activity
listenerActivity.onAlbumSelected(tag.albumId, tag.albumTitle);
listenerActivity.onAlbumSelected(tag);
}
};
}
@ -194,6 +196,7 @@ public class AlbumListFragment extends AbstractListFragment {
MediaContract.Albums.GENRE,
MediaContract.Albums.THUMBNAIL,
MediaContract.Albums.YEAR,
MediaContract.Albums.RATING,
};
String SORT = MediaDatabase.sortCommonTokens(MediaContract.Albums.TITLE) + " ASC";
@ -205,6 +208,7 @@ public class AlbumListFragment extends AbstractListFragment {
final int GENRE = 4;
final int THUMBNAIL = 5;
final int YEAR = 6;
final int RATING = 7;
}
private class AlbumsAdapter extends CursorAdapter {
@ -220,10 +224,8 @@ public class AlbumListFragment extends AbstractListFragment {
// Use the same dimensions as in the details fragment, so that it hits Picasso's cache when
// the user transitions to that fragment, avoiding another call and imediatelly showing the image
Resources resources = context.getResources();
artWidth = (int)(resources.getDimension(R.dimen.albumdetail_poster_width) /
UIUtils.IMAGE_RESIZE_FACTOR);
artHeight = (int)(resources.getDimension(R.dimen.albumdetail_poster_heigth) /
UIUtils.IMAGE_RESIZE_FACTOR);
artWidth = (int) resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_width);
artHeight = (int) resources.getDimensionPixelOffset(R.dimen.albumdetail_poster_heigth);
}
/** {@inheritDoc} */
@ -244,12 +246,17 @@ public class AlbumListFragment extends AbstractListFragment {
}
/** {@inheritDoc} */
@TargetApi(21)
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ViewHolder viewHolder = (ViewHolder)view.getTag();
viewHolder.albumId = cursor.getInt(AlbumListQuery.ALBUMID);
viewHolder.albumTitle = cursor.getString(AlbumListQuery.TITLE);
viewHolder.albumArtist = cursor.getString(AlbumListQuery.DISPLAYARTIST);
viewHolder.albumGenre = cursor.getString(AlbumListQuery.GENRE);
viewHolder.albumYear = cursor.getInt(AlbumListQuery.YEAR);
viewHolder.albumRating = cursor.getDouble(AlbumListQuery.RATING);
viewHolder.titleView.setText(viewHolder.albumTitle);
viewHolder.artistView.setText(cursor.getString(AlbumListQuery.DISPLAYARTIST));
@ -269,13 +276,17 @@ public class AlbumListFragment extends AbstractListFragment {
ImageView contextMenu = (ImageView)view.findViewById(R.id.list_context_menu);
contextMenu.setTag(viewHolder);
contextMenu.setOnClickListener(albumlistItemMenuClickListener);
if(Utils.isLollipopOrLater()) {
viewHolder.artView.setTransitionName("a"+viewHolder.albumId);
}
}
}
/**
* View holder pattern
*/
private static class ViewHolder {
public static class ViewHolder {
TextView titleView;
TextView artistView;
TextView genresView;
@ -283,6 +294,10 @@ public class AlbumListFragment extends AbstractListFragment {
int albumId;
String albumTitle;
String albumArtist;
int albumYear;
String albumGenre;
double albumRating;
}
private View.OnClickListener albumlistItemMenuClickListener = new View.OnClickListener() {

View File

@ -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;
@ -243,29 +244,44 @@ public class MusicActivity extends BaseActivity
}
@TargetApi(21)
public void onArtistSelected(int artistId, String artistName) {
selectedArtistId = artistId;
selectedArtistName = artistName;
// Replace list fragment
AlbumListFragment albumListFragment = AlbumListFragment.newInstanceForArtist(artistId);
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.fragment_details_enter, 0, R.anim.fragment_list_popenter, 0)
.replace(R.id.fragment_container, albumListFragment)
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
// Setup animations
if (Utils.isLollipopOrLater()) {
//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);
albumListFragment.setExitTransition(fade);
albumListFragment.setReenterTransition(fade);
albumListFragment.setSharedElementReturnTransition(TransitionInflater.from(
this).inflateTransition(R.transition.change_image));
} else {
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0, R.anim.fragment_list_popenter, 0);
}
fragTrans.replace(R.id.fragment_container, albumListFragment)
.addToBackStack(null)
.commit();
navigationDrawerFragment.animateDrawerToggle(true);
setupActionBar(null, artistName, null, null);
}
@TargetApi(21)
public void onAlbumSelected(int albumId, String albumTitle) {
selectedAlbumId = albumId;
selectedAlbumTitle = albumTitle;
public void onAlbumSelected(AlbumListFragment.ViewHolder vh) {
selectedAlbumId = vh.albumId;
selectedAlbumTitle = vh.albumTitle;
// Replace list fragment
AlbumDetailsFragment albumDetailsFragment = AlbumDetailsFragment.newInstance(albumId);
AlbumDetailsFragment albumDetailsFragment = AlbumDetailsFragment.newInstance(vh);
FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction();
// Set up transitions
@ -274,15 +290,22 @@ public class MusicActivity extends BaseActivity
.from(this)
.inflateTransition(R.transition.media_details));
albumDetailsFragment.setReturnTransition(null);
Transition changeImageTransition = TransitionInflater.from(
this).inflateTransition(R.transition.change_image);
albumDetailsFragment.setSharedElementReturnTransition(changeImageTransition);
albumDetailsFragment.setSharedElementEnterTransition(changeImageTransition);
fragTrans.addSharedElement(vh.artView, vh.artView.getTransitionName());
} else {
fragTrans.setCustomAnimations(R.anim.fragment_details_enter, 0,
R.anim.fragment_list_popenter, 0);
}
fragTrans.replace(R.id.fragment_container, albumDetailsFragment)
.addToBackStack(null)
.addToBackStack(null)
.commit();
setupActionBar(albumTitle, null, null, null);
setupActionBar(selectedAlbumTitle, null, null, null);
}
public void onAudioGenreSelected(int genreId, String genreTitle) {
@ -321,7 +344,7 @@ public class MusicActivity extends BaseActivity
}
fragTrans.replace(R.id.fragment_container, detailsFragment)
.addToBackStack(null)
.addToBackStack(null)
.commit();
setupActionBar(null, null, null, musicVideoTitle);
}

View File

@ -110,8 +110,7 @@
android:layout_height="match_parent"
style="@style/Widget.Button.Borderless"
android:src="?attr/iconDownload"
android:contentDescription="@string/download"
android:visibility="gone"/>
android:contentDescription="@string/download"/>
</LinearLayout>
<RelativeLayout