From f84ccaf02a4fdee890a8e90e6e71caf4636de99c Mon Sep 17 00:00:00 2001 From: Martijn Brekhof Date: Thu, 10 Dec 2015 16:44:42 +0100 Subject: [PATCH] Implemented fix for movie and album shared element transitions When shared element view is not visible on returning, the return shared element transition starts off screen. This looks odd and we now simply do not perform the shared element transition when the shared element is not visible. --- .../xbmc/kore/ui/AlbumDetailsFragment.java | 12 ++++++ .../xbmc/kore/ui/MovieDetailsFragment.java | 14 ++++++- .../java/org/xbmc/kore/ui/MoviesActivity.java | 38 ++++++++++++++++++- .../java/org/xbmc/kore/ui/MusicActivity.java | 38 ++++++++++++++++++- .../java/org/xbmc/kore/utils/UIUtils.java | 28 +++++++------- 5 files changed, 113 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java b/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java index 45f8c60..9333406 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AlbumDetailsFragment.java @@ -669,6 +669,18 @@ public class AlbumDetailsFragment extends Fragment } } + /** + * Returns the shared element if visible + * @return View if visible, null otherwise + */ + public View getSharedElement() { + if (UIUtils.isViewInBounds(mediaPanel, mediaPoster)) { + return mediaPoster; + } + + return null; + } + /** * Album details query parameters. */ diff --git a/app/src/main/java/org/xbmc/kore/ui/MovieDetailsFragment.java b/app/src/main/java/org/xbmc/kore/ui/MovieDetailsFragment.java index e657384..0c40f61 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MovieDetailsFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/MovieDetailsFragment.java @@ -201,7 +201,7 @@ public class MovieDetailsFragment extends AbstractDetailsFragment // Pad main content view to overlap with bottom system bar // UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true); // mediaPanel.setClipToPadding(false); - + return root; } @@ -628,6 +628,18 @@ public class MovieDetailsFragment extends AbstractDetailsFragment } } + /** + * Returns the shared element if visible + * @return View if visible, null otherwise + */ + public View getSharedElement() { + if (UIUtils.isViewInBounds(mediaPanel, mediaPoster)) { + return mediaPoster; + } + + return null; + } + /** * Movie details query parameters. */ diff --git a/app/src/main/java/org/xbmc/kore/ui/MoviesActivity.java b/app/src/main/java/org/xbmc/kore/ui/MoviesActivity.java index 792559f..faa6088 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MoviesActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/MoviesActivity.java @@ -26,12 +26,16 @@ import android.transition.Transition; import android.transition.TransitionInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.Window; import org.xbmc.kore.R; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.Utils; +import java.util.List; +import java.util.Map; + /** * Controls the presentation of Movies information (list, details) * All the information is presented by specific fragments @@ -48,6 +52,8 @@ public class MoviesActivity extends BaseActivity private NavigationDrawerFragment navigationDrawerFragment; + private boolean clearSharedElements; + @TargetApi(21) @Override protected void onCreate(Bundle savedInstanceState) { @@ -76,6 +82,18 @@ public class MoviesActivity extends BaseActivity movieListFragment.setReenterTransition(fade); movieListFragment.setSharedElementReturnTransition(TransitionInflater.from( this).inflateTransition(R.transition.change_image)); + + android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() { + @Override + public void onMapSharedElements(List names, Map sharedElements) { + if (clearSharedElements) { + names.clear(); + sharedElements.clear(); + clearSharedElements = false; + } + } + }; + movieListFragment.setExitSharedElementCallback(seCallback); } getSupportFragmentManager() .beginTransaction() @@ -189,9 +207,27 @@ public class MoviesActivity extends BaseActivity // Set up transitions if (Utils.isLollipopOrLater()) { - MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh); + final MovieDetailsFragment movieDetailsFragment = MovieDetailsFragment.newInstance(vh); FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction(); + android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() { + @Override + public void onMapSharedElements(List names, Map sharedElements) { + //On returning onMapSharedElements for the exiting fragment is called before the onMapSharedElements + // for the reentering fragment. We use this to determine if we are returning and if + // we should clear the shared element lists. Note that, clearing must be done in the reentering fragment + // as this is called last. Otherwise it the app will crash during transition setup. Not sure, but might + // be a v4 support package bug. + if (movieDetailsFragment.isVisible()) { + View sharedView = movieDetailsFragment.getSharedElement(); + if (sharedView == null) { // shared element not visible + clearSharedElements = true; + } + } + } + }; + movieDetailsFragment.setEnterSharedElementCallback(seCallback); + movieDetailsFragment.setEnterTransition(TransitionInflater .from(this) .inflateTransition(R.transition.media_details)); diff --git a/app/src/main/java/org/xbmc/kore/ui/MusicActivity.java b/app/src/main/java/org/xbmc/kore/ui/MusicActivity.java index e5306ba..1cc0e46 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MusicActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/MusicActivity.java @@ -26,11 +26,15 @@ import android.transition.Transition; import android.transition.TransitionInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import org.xbmc.kore.R; import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.Utils; +import java.util.List; +import java.util.Map; + /** * Controls the presentation of Music information (list, details) * All the information is presented by specific fragments @@ -62,6 +66,8 @@ public class MusicActivity extends BaseActivity private NavigationDrawerFragment navigationDrawerFragment; + private boolean clearSharedElements; + @TargetApi(21) @Override protected void onCreate(Bundle savedInstanceState) { @@ -255,6 +261,18 @@ public class MusicActivity extends BaseActivity FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction(); // Setup animations if (Utils.isLollipopOrLater()) { + android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() { + @Override + public void onMapSharedElements(List names, Map sharedElements) { + if (clearSharedElements) { + names.clear(); + sharedElements.clear(); + clearSharedElements = false; + } + } + }; + albumListFragment.setExitSharedElementCallback(seCallback); + //Fade added to prevent shared element from disappearing very shortly at the start of the transition. Transition fade = TransitionInflater .from(this) @@ -281,11 +299,29 @@ public class MusicActivity extends BaseActivity selectedAlbumTitle = vh.albumTitle; // Replace list fragment - AlbumDetailsFragment albumDetailsFragment = AlbumDetailsFragment.newInstance(vh); + final AlbumDetailsFragment albumDetailsFragment = AlbumDetailsFragment.newInstance(vh); FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction(); // Set up transitions if (Utils.isLollipopOrLater()) { + android.support.v4.app.SharedElementCallback seCallback = new android.support.v4.app.SharedElementCallback() { + @Override + public void onMapSharedElements(List names, Map sharedElements) { + //On returning onMapSharedElements for the exiting fragment is called before the onMapSharedElements + // for the reentering fragment. We use this to determine if we are returning and if + // we should clear the shared element lists. Note that, clearing must be done in the reentering fragment + // as this is called last. Otherwise it the app will crash during transition setup. Not sure, but might + // be a v4 support package bug. + if (albumDetailsFragment.isVisible()) { + View sharedView = albumDetailsFragment.getSharedElement(); + if (sharedView == null) { // shared element not visible + clearSharedElements = true; + } + } + } + }; + albumDetailsFragment.setEnterSharedElementCallback(seCallback); + albumDetailsFragment.setEnterTransition(TransitionInflater .from(this) .inflateTransition(R.transition.media_details)); diff --git a/app/src/main/java/org/xbmc/kore/utils/UIUtils.java b/app/src/main/java/org/xbmc/kore/utils/UIUtils.java index b28160a..1b6cf0e 100644 --- a/app/src/main/java/org/xbmc/kore/utils/UIUtils.java +++ b/app/src/main/java/org/xbmc/kore/utils/UIUtils.java @@ -122,16 +122,16 @@ public class UIUtils { if ((imageWidth) > 0 && (imageHeight > 0)) { hostManager.getPicasso() - .load(hostManager.getHostInfo().getImageUrl(imageUrl)) - .resize(imageWidth, imageHeight) - .centerCrop() - .into(imageView); + .load(hostManager.getHostInfo().getImageUrl(imageUrl)) + .resize(imageWidth, imageHeight) + .centerCrop() + .into(imageView); } else { hostManager.getPicasso() - .load(hostManager.getHostInfo().getImageUrl(imageUrl)) - .fit() - .centerCrop() - .into(imageView); + .load(hostManager.getHostInfo().getImageUrl(imageUrl)) + .fit() + .centerCrop() + .into(imageView); } } @@ -191,11 +191,11 @@ public class UIUtils { } char charAvatar = TextUtils.isEmpty(str) ? - ' ' : str.charAt(0); + ' ' : str.charAt(0); avatarColorsIdx = TextUtils.isEmpty(str) ? 0 : - Math.max(Character.getNumericValue(str.charAt(0)) + - Character.getNumericValue(str.charAt(str.length() - 1)) + - str.length(), 0) % characterAvatarColors.length(); + Math.max(Character.getNumericValue(str.charAt(0)) + + Character.getNumericValue(str.charAt(str.length() - 1)) + + str.length(), 0) % characterAvatarColors.length(); int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000); // avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length()); return new CharacterDrawable(charAvatar, color); @@ -318,7 +318,7 @@ public class UIUtils { @Override public void run() { NetUtils.sendWolMagicPacket(hostInfo.getMacAddress(), - hostInfo.getAddress(), hostInfo.getWolPort()); + hostInfo.getAddress(), hostInfo.getWolPort()); } }).start(); Toast.makeText(context, R.string.wol_sent, Toast.LENGTH_SHORT).show(); @@ -423,7 +423,7 @@ public class UIUtils { // Show the animation int endRadius = Math.max(exitTransitionView.getHeight(), exitTransitionView.getWidth()); Animator exitAnim = ViewAnimationUtils.createCircularReveal(exitTransitionView, - centerX, centerY, 0, endRadius); + centerX, centerY, 0, endRadius); exitAnim.setDuration(200); exitAnim.addListener(new Animator.AnimatorListener() {