mirror of https://github.com/KokaKiwi/BarInsta
305 lines
12 KiB
Java
305 lines
12 KiB
Java
package awais.instagrabber.customviews;
|
|
|
|
import android.animation.Animator;
|
|
import android.graphics.Rect;
|
|
import android.os.Handler;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewTreeObserver;
|
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.fragment.app.DialogFragment;
|
|
import androidx.transition.ChangeBounds;
|
|
import androidx.transition.ChangeTransform;
|
|
import androidx.transition.Transition;
|
|
import androidx.transition.TransitionListenerAdapter;
|
|
import androidx.transition.TransitionManager;
|
|
import androidx.transition.TransitionSet;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import awais.instagrabber.utils.Utils;
|
|
|
|
public abstract class SharedElementTransitionDialogFragment extends DialogFragment {
|
|
// private static final String TAG = "SETDialogFragment";
|
|
private static final int DURATION = 200;
|
|
|
|
private final Map<Integer, View> startViews = new HashMap<>();
|
|
private final Map<Integer, View> destViews = new HashMap<>();
|
|
private final Map<Integer, ViewBounds> viewBoundsMap = new HashMap<>();
|
|
private final List<Animator> additionalAnimators = new ArrayList<>();
|
|
private final Handler initialBoundsHandler = new Handler();
|
|
|
|
private boolean startCalled = false;
|
|
private boolean startInitiated = false;
|
|
private int boundsCalculatedCount = 0;
|
|
|
|
protected int getAnimationDuration() {
|
|
return DURATION;
|
|
}
|
|
|
|
public void addSharedElement(@NonNull final View startView, @NonNull final View destView) {
|
|
final int key = destView.hashCode();
|
|
startViews.put(key, startView);
|
|
destViews.put(key, destView);
|
|
initialBoundsHandler.post(() -> setupInitialBounds(startView, destView));
|
|
}
|
|
|
|
public void startPostponedEnterTransition() {
|
|
startCalled = true;
|
|
if (startInitiated) return;
|
|
if (boundsCalculatedCount < startViews.size()) return;
|
|
startInitiated = true;
|
|
final Set<Integer> keySet = startViews.keySet();
|
|
final View view = getView();
|
|
if (!(view instanceof ViewGroup)) return;
|
|
final TransitionSet transitionSet = new TransitionSet()
|
|
.setDuration(DURATION)
|
|
.setInterpolator(new AccelerateDecelerateInterpolator())
|
|
.addTransition(new ChangeBounds())
|
|
.addTransition(new ChangeTransform())
|
|
.addListener(new TransitionListenerAdapter() {
|
|
@Override
|
|
public void onTransitionStart(@NonNull final Transition transition) {
|
|
for (Animator animator : additionalAnimators) {
|
|
animator.start();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTransitionEnd(@NonNull final Transition transition) {
|
|
for (final Integer key : keySet) {
|
|
final View startView = startViews.get(key);
|
|
final View destView = destViews.get(key);
|
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
|
if (startView == null || destView == null || viewBounds == null) return;
|
|
onEndSharedElementAnimation(startView, destView, viewBounds);
|
|
}
|
|
}
|
|
});
|
|
view.post(() -> {
|
|
TransitionManager.beginDelayedTransition((ViewGroup) view, transitionSet);
|
|
for (final Integer key : keySet) {
|
|
final View startView = startViews.get(key);
|
|
final View destView = destViews.get(key);
|
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
|
if (startView == null || destView == null || viewBounds == null) return;
|
|
onBeforeSharedElementAnimation(startView, destView, viewBounds);
|
|
setDestBounds(key);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void setDestBounds(final int key) {
|
|
final View startView = startViews.get(key);
|
|
if (startView == null) return;
|
|
final View destView = destViews.get(key);
|
|
if (destView == null) return;
|
|
final ViewBounds viewBounds = viewBoundsMap.get(key);
|
|
if (viewBounds == null) return;
|
|
destView.setX((int) viewBounds.getDestX());
|
|
destView.setY((int) viewBounds.getDestY());
|
|
destView.setTranslationX(0);
|
|
destView.setTranslationY(0);
|
|
final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
|
|
layoutParams.height = viewBounds.getDestHeight();
|
|
layoutParams.width = viewBounds.getDestWidth();
|
|
destView.requestLayout();
|
|
}
|
|
|
|
protected void onBeforeSharedElementAnimation(@NonNull final View startView,
|
|
@NonNull final View destView,
|
|
@NonNull final ViewBounds viewBounds) {}
|
|
|
|
protected void onEndSharedElementAnimation(@NonNull final View startView,
|
|
@NonNull final View destView,
|
|
@NonNull final ViewBounds viewBounds) {}
|
|
|
|
private void setupInitialBounds(@NonNull final View startView, @NonNull final View destView) {
|
|
final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
|
|
private boolean firstPassDone;
|
|
|
|
@Override
|
|
public boolean onPreDraw() {
|
|
destView.getViewTreeObserver().removeOnPreDrawListener(this);
|
|
if (!firstPassDone) {
|
|
getViewBounds(startView, destView, this);
|
|
firstPassDone = true;
|
|
return false;
|
|
}
|
|
final int[] location = new int[2];
|
|
startView.getLocationOnScreen(location);
|
|
final int initX = location[0];
|
|
final int initY = location[1];
|
|
destView.setX(initX);
|
|
destView.setY(initY - Utils.getStatusBarHeight(getContext()));
|
|
destView.requestLayout();
|
|
boundsCalculatedCount++;
|
|
if (startCalled) {
|
|
startPostponedEnterTransition();
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
|
}
|
|
|
|
private void getViewBounds(@NonNull final View startView,
|
|
@NonNull final View destView,
|
|
@NonNull final ViewTreeObserver.OnPreDrawListener preDrawListener) {
|
|
final ViewBounds viewBounds = new ViewBounds();
|
|
viewBounds.setDestWidth(destView.getWidth());
|
|
viewBounds.setDestHeight(destView.getHeight());
|
|
viewBounds.setDestX(destView.getX());
|
|
viewBounds.setDestY(destView.getY());
|
|
|
|
final Rect destBounds = new Rect();
|
|
destView.getDrawingRect(destBounds);
|
|
viewBounds.setDestBounds(destBounds);
|
|
|
|
final ViewGroup.LayoutParams layoutParams = destView.getLayoutParams();
|
|
|
|
final Rect startBounds = new Rect();
|
|
startView.getDrawingRect(startBounds);
|
|
viewBounds.setStartBounds(startBounds);
|
|
|
|
final int key = destView.hashCode();
|
|
viewBoundsMap.put(key, viewBounds);
|
|
|
|
layoutParams.height = startView.getHeight();
|
|
layoutParams.width = startView.getWidth();
|
|
|
|
destView.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
|
|
destView.requestLayout();
|
|
}
|
|
|
|
// private void animateBounds(@NonNull final View startView,
|
|
// @NonNull final View destView,
|
|
// @NonNull final ViewBounds viewBounds) {
|
|
// final ValueAnimator heightAnimator = ObjectAnimator.ofInt(startView.getHeight(), viewBounds.getDestHeight());
|
|
// final ValueAnimator widthAnimator = ObjectAnimator.ofInt(startView.getWidth(), viewBounds.getDestWidth());
|
|
// heightAnimator.setDuration(DURATION);
|
|
// widthAnimator.setDuration(DURATION);
|
|
// additionalAnimators.add(heightAnimator);
|
|
// additionalAnimators.add(widthAnimator);
|
|
// heightAnimator.addUpdateListener(animation -> {
|
|
// ViewGroup.LayoutParams params = destView.getLayoutParams();
|
|
// params.height = (int) animation.getAnimatedValue();
|
|
// destView.requestLayout();
|
|
// });
|
|
// widthAnimator.addUpdateListener(animation -> {
|
|
// ViewGroup.LayoutParams params = destView.getLayoutParams();
|
|
// params.width = (int) animation.getAnimatedValue();
|
|
// destView.requestLayout();
|
|
// });
|
|
// onBeforeSharedElementAnimation(startView, destView, viewBounds);
|
|
// final float destX = viewBounds.getDestX();
|
|
// final float destY = viewBounds.getDestY();
|
|
// final AnimatorSet animatorSet = new AnimatorSet();
|
|
// animatorSet.addListener(new AnimatorListenerAdapter() {
|
|
// @Override
|
|
// public void onAnimationEnd(final Animator animation) {
|
|
// animationEnded(startView, destView, viewBounds);
|
|
// }
|
|
// });
|
|
//
|
|
// destView.animate()
|
|
// .x(destX)
|
|
// .y(destY)
|
|
// .setDuration(DURATION)
|
|
// .withStartAction(() -> {
|
|
// if (!additionalAnimatorsStarted && additionalAnimators.size() > 0) {
|
|
// additionalAnimatorsStarted = true;
|
|
// animatorSet.playTogether(additionalAnimators);
|
|
// animatorSet.start();
|
|
// }
|
|
// })
|
|
// .withEndAction(() -> animationEnded(startView, destView, viewBounds))
|
|
// .start();
|
|
// }
|
|
|
|
// private int endCount = 0;
|
|
// private void animationEnded(final View startView, final View destView, final ViewBounds viewBounds) {
|
|
// ++endCount;
|
|
// if (endCount != startViews.size() + 1) return;
|
|
// onEndSharedElementAnimation(startView, destView, viewBounds);
|
|
// }
|
|
|
|
protected void addAnimator(@NonNull final Animator animator) {
|
|
additionalAnimators.add(animator);
|
|
}
|
|
|
|
protected static class ViewBounds {
|
|
private float destY;
|
|
private float destX;
|
|
private int destHeight;
|
|
private int destWidth;
|
|
private Rect startBounds;
|
|
private Rect destBounds;
|
|
|
|
public ViewBounds() {}
|
|
|
|
public float getDestY() {
|
|
return destY;
|
|
}
|
|
|
|
public void setDestY(final float destY) {
|
|
this.destY = destY;
|
|
}
|
|
|
|
public float getDestX() {
|
|
return destX;
|
|
}
|
|
|
|
public void setDestX(final float destX) {
|
|
this.destX = destX;
|
|
}
|
|
|
|
public int getDestHeight() {
|
|
return destHeight;
|
|
}
|
|
|
|
public void setDestHeight(final int destHeight) {
|
|
this.destHeight = destHeight;
|
|
}
|
|
|
|
public int getDestWidth() {
|
|
return destWidth;
|
|
}
|
|
|
|
public void setDestWidth(final int destWidth) {
|
|
this.destWidth = destWidth;
|
|
}
|
|
|
|
public Rect getStartBounds() {
|
|
return startBounds;
|
|
}
|
|
|
|
public void setStartBounds(final Rect startBounds) {
|
|
this.startBounds = startBounds;
|
|
}
|
|
|
|
public Rect getDestBounds() {
|
|
return destBounds;
|
|
}
|
|
|
|
public void setDestBounds(final Rect destBounds) {
|
|
this.destBounds = destBounds;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDestroyView() {
|
|
super.onDestroyView();
|
|
startViews.clear();
|
|
destViews.clear();
|
|
viewBoundsMap.clear();
|
|
additionalAnimators.clear();
|
|
}
|
|
}
|