Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6fd0ef7f authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Add Transitions useful for Activity transitions."

parents dde10d07 d6107a31
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -1020,6 +1020,7 @@ package android {
    field public static final int shrinkColumns = 16843082; // 0x101014a
    field public static final deprecated int singleLine = 16843101; // 0x101015d
    field public static final int singleUser = 16843711; // 0x10103bf
    field public static final int slideEdge = 16843835; // 0x101043b
    field public static final int smallIcon = 16843422; // 0x101029e
    field public static final int smallScreens = 16843396; // 0x1010284
    field public static final int smoothScrollbar = 16843313; // 0x1010231
@@ -27407,6 +27408,16 @@ package android.transition {
    method public void setResizeClip(boolean);
  }
  public class CircularPropagation extends android.transition.VisibilityPropagation {
    ctor public CircularPropagation();
    method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
    method public void setPropagationSpeed(float);
  }
  public class Explode extends android.transition.Visibility {
    ctor public Explode();
  }
  public class Fade extends android.transition.Visibility {
    ctor public Fade();
    ctor public Fade(int);
@@ -27414,6 +27425,12 @@ package android.transition {
    field public static final int OUT = 2; // 0x2
  }
  public class MoveImage extends android.transition.Transition {
    ctor public MoveImage();
    method public void captureEndValues(android.transition.TransitionValues);
    method public void captureStartValues(android.transition.TransitionValues);
  }
  public final class Scene {
    ctor public Scene(android.view.ViewGroup);
    ctor public Scene(android.view.ViewGroup, android.view.View);
@@ -27426,6 +27443,27 @@ package android.transition {
    method public void setExitAction(java.lang.Runnable);
  }
  public class SidePropagation extends android.transition.VisibilityPropagation {
    ctor public SidePropagation();
    method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
    method public void setPropagationSpeed(float);
    method public void setSide(int);
    field public static final int BOTTOM = 3; // 0x3
    field public static final int LEFT = 0; // 0x0
    field public static final int RIGHT = 2; // 0x2
    field public static final int TOP = 1; // 0x1
  }
  public class Slide extends android.transition.Visibility {
    ctor public Slide();
    ctor public Slide(int);
    method public void setSlideEdge(int);
    field public static final int BOTTOM = 3; // 0x3
    field public static final int LEFT = 0; // 0x0
    field public static final int RIGHT = 2; // 0x2
    field public static final int TOP = 1; // 0x1
  }
  public abstract class Transition implements java.lang.Cloneable {
    ctor public Transition();
    method public android.transition.Transition addListener(android.transition.Transition.TransitionListener);
@@ -27443,8 +27481,11 @@ package android.transition {
    method public android.transition.Transition excludeTarget(android.view.View, boolean);
    method public android.transition.Transition excludeTarget(java.lang.Class, boolean);
    method public long getDuration();
    method public android.graphics.Rect getEpicenter();
    method public android.transition.Transition.EpicenterCallback getEpicenterCallback();
    method public android.animation.TimeInterpolator getInterpolator();
    method public java.lang.String getName();
    method public android.transition.TransitionPropagation getPropagation();
    method public long getStartDelay();
    method public java.util.List<java.lang.Integer> getTargetIds();
    method public java.util.List<android.view.View> getTargets();
@@ -27454,10 +27495,17 @@ package android.transition {
    method public android.transition.Transition removeTarget(int);
    method public android.transition.Transition removeTarget(android.view.View);
    method public android.transition.Transition setDuration(long);
    method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback);
    method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator);
    method public void setPropagation(android.transition.TransitionPropagation);
    method public android.transition.Transition setStartDelay(long);
  }
  public static abstract class Transition.EpicenterCallback {
    ctor public Transition.EpicenterCallback();
    method public abstract android.graphics.Rect getEpicenter(android.transition.Transition);
  }
  public static abstract interface Transition.TransitionListener {
    method public abstract void onTransitionCancel(android.transition.Transition);
    method public abstract void onTransitionEnd(android.transition.Transition);
@@ -27484,6 +27532,13 @@ package android.transition {
    method public void transitionTo(android.transition.Scene);
  }
  public abstract class TransitionPropagation {
    ctor public TransitionPropagation();
    method public abstract void captureValues(android.transition.TransitionValues);
    method public abstract java.lang.String[] getPropagationProperties();
    method public abstract long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
  }
  public class TransitionSet extends android.transition.Transition {
    ctor public TransitionSet();
    method public android.transition.TransitionSet addTransition(android.transition.Transition);
@@ -27508,7 +27563,18 @@ package android.transition {
    method public void captureStartValues(android.transition.TransitionValues);
    method public boolean isVisible(android.transition.TransitionValues);
    method public android.animation.Animator onAppear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int);
    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues);
    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.transition.TransitionValues, int, android.transition.TransitionValues, int);
    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.transition.TransitionValues, android.transition.TransitionValues);
  }
  public abstract class VisibilityPropagation extends android.transition.TransitionPropagation {
    ctor public VisibilityPropagation();
    method public void captureValues(android.transition.TransitionValues);
    method public java.lang.String[] getPropagationProperties();
    method public int getViewVisibility(android.transition.TransitionValues);
    method public int getViewX(android.transition.TransitionValues);
    method public int getViewY(android.transition.TransitionValues);
  }
}
+12 −15
Original line number Diff line number Diff line
@@ -722,8 +722,6 @@ public class ActivityOptions {
    private static class ExitTransitionListener extends ResultReceiver
            implements Transition.TransitionListener {
        private boolean mSharedElementNotified;
        private Transition mExitTransition;
        private Transition mSharedElementTransition;
        private IRemoteCallback mTransitionCompleteCallback;
        private boolean mExitComplete;
        private boolean mSharedElementComplete;
@@ -733,10 +731,15 @@ public class ActivityOptions {
                SharedElementSource sharedElementSource) {
            super(null);
            mSharedElementSource = sharedElementSource;
            mExitTransition = exitTransition;
            mExitTransition.addListener(this);
            mSharedElementTransition = sharedElementTransition;
            mSharedElementTransition.addListener(this);
            exitTransition.addListener(this);
            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    mSharedElementComplete = true;
                    notifySharedElement();
                    transition.removeListener(this);
                }
            });
        }

        @Override
@@ -769,15 +772,9 @@ public class ActivityOptions {

        @Override
        public void onTransitionEnd(Transition transition) {
            if (transition == mExitTransition) {
            mExitComplete = true;
            notifyExit();
                mExitTransition.removeListener(this);
            } else {
                mSharedElementComplete = true;
                notifySharedElement();
                mSharedElementTransition.removeListener(this);
            }
            transition.removeListener(this);
        }

        @Override
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 android.transition;

import android.graphics.Rect;
import android.util.FloatMath;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * A propagation that varies with the distance to the epicenter of the Transition
 * or center of the scene if no epicenter exists. When a View is visible in the
 * start of the transition, Views farther from the epicenter will transition
 * sooner than Views closer to the epicenter. When a View is not in the start
 * of the transition or is not visible at the start of the transition, it will
 * transition sooner when closer to the epicenter and later when farther from
 * the epicenter. This is the default TransitionPropagation used with
 * {@link android.transition.Explode}.
 */
public class CircularPropagation extends VisibilityPropagation {
    private static final String TAG = "CircularPropagation";

    private float mPropagationSpeed = 4.0f;

    /**
     * Sets the speed at which transition propagation happens, relative to the duration of the
     * Transition. A <code>propagationSpeed</code> of 1 means that a View centered farthest from
     * the epicenter and View centered at the epicenter will have a difference
     * in start delay of approximately the duration of the Transition. A speed of 2 means the
     * start delay difference will be approximately half of the duration of the transition. A
     * value of 0 is illegal, but negative values will invert the propagation.
     *
     * @param propagationSpeed The speed at which propagation occurs, relative to the duration
     *                         of the transition. A speed of 4 means it works 4 times as fast
     *                         as the duration of the transition. May not be 0.
     */
    public void setPropagationSpeed(float propagationSpeed) {
        if (propagationSpeed == 0) {
            throw new IllegalArgumentException("propagationSpeed may not be 0");
        }
        mPropagationSpeed = propagationSpeed;
    }

    @Override
    public long getStartDelay(ViewGroup sceneRoot, Transition transition,
            TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null && endValues == null) {
            return 0;
        }
        int directionMultiplier = 1;
        TransitionValues positionValues;
        if (endValues == null || getViewVisibility(startValues) == View.VISIBLE) {
            positionValues = startValues;
            directionMultiplier = -1;
        } else {
            positionValues = endValues;
        }

        int viewCenterX = getViewX(positionValues);
        int viewCenterY = getViewY(positionValues);

        Rect epicenter = transition.getEpicenter();
        int epicenterX;
        int epicenterY;
        if (epicenter != null) {
            epicenterX = epicenter.centerX();
            epicenterY = epicenter.centerY();
        } else {
            int[] loc = new int[2];
            sceneRoot.getLocationOnScreen(loc);
            epicenterX = Math.round(loc[0] + (sceneRoot.getWidth() / 2)
                    + sceneRoot.getTranslationX());
            epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2)
                    + sceneRoot.getTranslationY());
        }
        float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY);
        float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
        float distanceFraction = distance/maxDistance;

        return Math.round(transition.getDuration() * directionMultiplier / mPropagationSpeed
                * distanceFraction);
    }

    private static float distance(float x1, float y1, float x2, float y2) {
        float x = x2 - x1;
        float y = y2 - y1;
        return FloatMath.sqrt((x * x) + (y * y));
    }
}
+228 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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 android.transition;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.FloatMath;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;

/**
 * This transition tracks changes to the visibility of target views in the
 * start and end scenes and moves views in or out from the edges of the
 * scene. Visibility is determined by both the
 * {@link View#setVisibility(int)} state of the view as well as whether it
 * is parented in the current view hierarchy. Disappearing Views are
 * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup,
 * TransitionValues, int, TransitionValues, int)}.
 * <p>Views move away from the focal View or the center of the Scene if
 * no epicenter was provided.</p>
 */
public class Explode extends Visibility {
    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
    private static final String TAG = "Explode";

    private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds";

    private int[] mTempLoc = new int[2];

    public Explode() {
        setPropagation(new CircularPropagation());
    }

    private void captureValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        view.getLocationOnScreen(mTempLoc);
        int left = mTempLoc[0] + Math.round(view.getTranslationX());
        int top = mTempLoc[1] + Math.round(view.getTranslationY());
        int right = left + view.getWidth();
        int bottom = top + view.getHeight();
        transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
        captureValues(transitionValues);
    }

    private Animator createAnimation(final View view, float startX, float startY, float endX,
            float endY, float terminalX, float terminalY, TimeInterpolator interpolator) {
        view.setTranslationX(startX);
        view.setTranslationY(startY);
        if (startY == endY && startX == endX) {
            return null;
        }
        Path path = new Path();
        path.moveTo(startX, startY);
        path.lineTo(endX, endY);
        ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
                View.TRANSLATION_Y, path);
        pathAnimator.setInterpolator(interpolator);
        OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY,
                endX, endY);
        pathAnimator.addListener(listener);
        pathAnimator.addPauseListener(listener);

        return pathAnimator;
    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
        if (endValues == null) {
            return null;
        }
        Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
        calculateOut(sceneRoot, bounds, mTempLoc);

        final float endX = view.getTranslationX();
        final float startX = endX + mTempLoc[0];
        final float endY = view.getTranslationY();
        final float startY = endY + mTempLoc[1];

        return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate);
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
        Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
        calculateOut(sceneRoot, bounds, mTempLoc);

        final float startX = view.getTranslationX();
        final float endX = startX + mTempLoc[0];
        final float startY = view.getTranslationY();
        final float endY = startY + mTempLoc[1];

        return createAnimation(view, startX, startY, endX, endY, startX, startY,
                sAccelerate);
    }

    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
        sceneRoot.getLocationOnScreen(mTempLoc);
        int sceneRootX = mTempLoc[0];
        int sceneRootY = mTempLoc[1];
        int focalX;
        int focalY;

        Rect epicenter = getEpicenter();
        if (epicenter == null) {
            focalX = sceneRootX + (sceneRoot.getWidth() / 2)
                    + Math.round(sceneRoot.getTranslationX());
            focalY = sceneRootY + (sceneRoot.getHeight() / 2)
                    + Math.round(sceneRoot.getTranslationY());
        } else {
            focalX = epicenter.centerX();
            focalY = epicenter.centerY();
        }

        int centerX = bounds.centerX();
        int centerY = bounds.centerY();
        float xVector = centerX - focalX;
        float yVector = centerY - focalY;

        if (xVector == 0 && yVector == 0) {
            // Random direction when View is centered on focal View.
            xVector = (float)(Math.random() * 2) - 1;
            yVector = (float)(Math.random() * 2) - 1;
        }
        float vectorSize = calculateDistance(xVector, yVector);
        xVector /= vectorSize;
        yVector /= vectorSize;

        float maxDistance =
                calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY);

        outVector[0] = Math.round(maxDistance * xVector);
        outVector[1] = Math.round(maxDistance * yVector);
    }

    private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) {
        int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX);
        int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY);
        return calculateDistance(maxX, maxY);
    }

    private static float calculateDistance(float x, float y) {
        return FloatMath.sqrt((x * x) + (y * y));
    }

    private static class OutAnimatorListener extends AnimatorListenerAdapter {
        private final View mView;
        private boolean mCanceled = false;
        private float mPausedX;
        private float mPausedY;
        private final float mTerminalX;
        private final float mTerminalY;
        private final float mEndX;
        private final float mEndY;

        public OutAnimatorListener(View view, float terminalX, float terminalY,
                float endX, float endY) {
            mView = view;
            mTerminalX = terminalX;
            mTerminalY = terminalY;
            mEndX = endX;
            mEndY = endY;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            mView.setTranslationX(mTerminalX);
            mView.setTranslationY(mTerminalY);
            mCanceled = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            if (!mCanceled) {
                mView.setTranslationX(mTerminalX);
                mView.setTranslationY(mTerminalY);
            }
        }

        @Override
        public void onAnimationPause(Animator animator) {
            mPausedX = mView.getTranslationX();
            mPausedY = mView.getTranslationY();
            mView.setTranslationY(mEndX);
            mView.setTranslationY(mEndY);
        }

        @Override
        public void onAnimationResume(Animator animator) {
            mView.setTranslationX(mPausedX);
            mView.setTranslationY(mPausedY);
        }
    }
}
+46 −212

File changed.

Preview size limit exceeded, changes collapsed.

Loading