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

Commit d6107a31 authored by George Mount's avatar George Mount
Browse files

Add Transitions useful for Activity transitions.

Slide: transition in and out of the edge of the scene.
Explode: transition to the scene borders

Moved capability from Fade to Visibility.

Change-Id: Ibeb0d8f751c990edc467570d9665fbe251af2703
parent cb4b7d99
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