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

Commit 7b75062f authored by George Mount's avatar George Mount
Browse files

Allow changing order of matching Views in Transitions.

Bug 14899804

Change-Id: I13c080141fce6be8430d540e3c42d40a445bb224
parent 11d04154
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -798,6 +798,7 @@ package android {
    field public static final int manageSpaceActivity = 16842756; // 0x1010004
    field public static final int mapViewStyle = 16842890; // 0x101008a
    field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
    field public static final int matchOrder = 16843861; // 0x1010455
    field public static final int max = 16843062; // 0x1010136
    field public static final int maxDate = 16843584; // 0x1010340
    field public static final int maxEms = 16843095; // 0x1010157
@@ -29092,8 +29093,13 @@ package android.transition {
    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 setMatchOrder(int...);
    method public void setPropagation(android.transition.TransitionPropagation);
    method public android.transition.Transition setStartDelay(long);
    field public static final int MATCH_ID = 3; // 0x3
    field public static final int MATCH_INSTANCE = 1; // 0x1
    field public static final int MATCH_ITEM_ID = 4; // 0x4
    field public static final int MATCH_VIEW_NAME = 2; // 0x2
  }
  public static abstract class Transition.EpicenterCallback {
+114 −14
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.view.SurfaceView;
import android.view.TextureView;
@@ -108,6 +107,40 @@ public abstract class Transition implements Cloneable {
    private static final String LOG_TAG = "Transition";
    static final boolean DBG = false;

    /**
     * With {@link #setMatchOrder(int...)}, chooses to match by View instance.
     */
    public static final int MATCH_INSTANCE = 0x1;
    private static final int MATCH_FIRST = MATCH_INSTANCE;

    /**
     * With {@link #setMatchOrder(int...)}, chooses to match by
     * {@link android.view.View#getViewName()}. Null names will not be matched.
     */
    public static final int MATCH_VIEW_NAME = 0x2;

    /**
     * With {@link #setMatchOrder(int...)}, chooses to match by
     * {@link android.view.View#getId()}. Negative IDs will not be matched.
     */
    public static final int MATCH_ID = 0x3;

    /**
     * With {@link #setMatchOrder(int...)}, chooses to match by the {@link android.widget.Adapter}
     * item id. When {@link android.widget.Adapter#hasStableIds()} returns false, no match
     * will be made for items.
     */
    public static final int MATCH_ITEM_ID = 0x4;

    private static final int MATCH_LAST = MATCH_ITEM_ID;

    private static final int[] DEFAULT_MATCH_ORDER = {
        MATCH_VIEW_NAME,
        MATCH_INSTANCE,
        MATCH_ID,
        MATCH_ITEM_ID,
    };

    private String mName = getClass().getName();

    long mStartDelay = -1;
@@ -127,6 +160,7 @@ public abstract class Transition implements Cloneable {
    private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
    private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
    TransitionSet mParent = null;
    private int[] mMatchOrder = DEFAULT_MATCH_ORDER;

    // Per-animator information used for later canceling when future transitions overlap
    private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
@@ -337,6 +371,53 @@ public abstract class Transition implements Cloneable {
        return null;
    }

    /**
     * Sets the order in which Transition matches View start and end values.
     * <p>
     * The default behavior is to match first by {@link android.view.View#getViewName()},
     * then by View instance, then by {@link android.view.View#getId()} and finally
     * by its item ID if it is in a direct child of ListView. The caller can
     * choose to have only some or all of the values of {@link #MATCH_INSTANCE},
     * {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
     * the match algorithms supplied will be used to determine whether Views are the
     * the same in both the start and end Scene. Views that do not match will be considered
     * as entering or leaving the Scene.
     * </p>
     * @param matches A list of zero or more of {@link #MATCH_INSTANCE},
     *                {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
     *                If none are provided, then the default match order will be set.
     */
    public void setMatchOrder(int... matches) {
        if (matches == null || matches.length == 0) {
            mMatchOrder = DEFAULT_MATCH_ORDER;
        } else {
            for (int i = 0; i < matches.length; i++) {
                int match = matches[i];
                if (!isValidMatch(match)) {
                    throw new IllegalArgumentException("matches contains invalid value");
                }
                if (alreadyContains(matches, i)) {
                    throw new IllegalArgumentException("matches contains a duplicate value");
                }
            }
            mMatchOrder = matches.clone();
        }
    }

    private static boolean isValidMatch(int match) {
        return (match >= MATCH_FIRST && match <= MATCH_LAST);
    }

    private static boolean alreadyContains(int[] array, int searchIndex) {
        int value = array[searchIndex];
        for (int i = 0; i < searchIndex; i++) {
            if (array[i] == value) {
                return true;
            }
        }
        return false;
    }

    /**
     * Match start/end values by View instance. Adds matched values to startValuesList
     * and endValuesList and removes them from unmatchedStart and unmatchedEnd.
@@ -464,6 +545,37 @@ public abstract class Transition implements Cloneable {
        }
    }

    private void matchStartAndEnd(TransitionValuesMaps startValues,
            TransitionValuesMaps endValues,
            ArrayList<TransitionValues> startValuesList,
            ArrayList<TransitionValues> endValuesList) {
        ArrayMap<View, TransitionValues> unmatchedStart =
                new ArrayMap<View, TransitionValues>(startValues.viewValues);
        ArrayMap<View, TransitionValues> unmatchedEnd =
                new ArrayMap<View, TransitionValues>(endValues.viewValues);

        for (int i = 0; i < mMatchOrder.length; i++) {
            switch (mMatchOrder[i]) {
                case MATCH_INSTANCE:
                    matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
                    break;
                case MATCH_VIEW_NAME:
                    matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                            startValues.nameValues, endValues.nameValues);
                    break;
                case MATCH_ID:
                    matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                            startValues.idValues, endValues.idValues);
                    break;
                case MATCH_ITEM_ID:
                    matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                            startValues.itemIdValues, endValues.itemIdValues);
                    break;
            }
        }
        addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
    }

    /**
     * This method, essentially a wrapper around all calls to createAnimator for all
     * possible target views, is called with the entire set of start/end
@@ -480,21 +592,9 @@ public abstract class Transition implements Cloneable {
        if (DBG) {
            Log.d(LOG_TAG, "createAnimators() for " + this);
        }
        ArrayMap<View, TransitionValues> unmatchedStart =
                new ArrayMap<View, TransitionValues>(startValues.viewValues);
        ArrayMap<View, TransitionValues> unmatchedEnd =
                new ArrayMap<View, TransitionValues>(endValues.viewValues);

        ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
        ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
        matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                startValues.nameValues, endValues.nameValues);
        matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
        matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                startValues.idValues, endValues.idValues);
        matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                startValues.itemIdValues, endValues.itemIdValues);
        addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
        matchStartAndEnd(startValues, endValues, startValuesList, endValuesList);

        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
        long minStartDelay = Long.MAX_VALUE;
+37 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

/**
 * This class inflates scenes and transitions from resource files.
@@ -40,6 +41,10 @@ import java.util.ArrayList;
 * and {@link android.R.styleable#TransitionManager}.
 */
public class TransitionInflater {
    private static final String MATCH_INSTANCE = "instance";
    private static final String MATCH_VIEW_NAME = "viewName";
    private static final String MATCH_ID = "id";
    private static final String MATCH_ITEM_ID = "itemId";

    private Context mContext;

@@ -266,6 +271,33 @@ public class TransitionInflater {
        }
    }

    private int[] parseMatchOrder(String matchOrderString) {
        StringTokenizer st = new StringTokenizer(matchOrderString, ",");
        int matches[] = new int[st.countTokens()];
        int index = 0;
        while (st.hasMoreTokens()) {
            String token = st.nextToken().trim();
            if (MATCH_ID.equalsIgnoreCase(token)) {
                matches[index] = Transition.MATCH_ID;
            } else if (MATCH_INSTANCE.equalsIgnoreCase(token)) {
                matches[index] = Transition.MATCH_INSTANCE;
            } else if (MATCH_VIEW_NAME.equalsIgnoreCase(token)) {
                matches[index] = Transition.MATCH_VIEW_NAME;
            } else if (MATCH_ITEM_ID.equalsIgnoreCase(token)) {
                matches[index] = Transition.MATCH_ITEM_ID;
            } else if (token.isEmpty()) {
                int[] smallerMatches = new int[matches.length - 1];
                System.arraycopy(matches, 0, smallerMatches, 0, index);
                matches = smallerMatches;
                index--;
            } else {
                throw new RuntimeException("Unknown match type in matchOrder: '" + token + "'");
            }
            index++;
        }
        return matches;
    }

    private Transition loadTransition(Transition transition, AttributeSet attrs)
            throws Resources.NotFoundException {

@@ -284,6 +316,11 @@ public class TransitionInflater {
        if (resID > 0) {
            transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
        }
        String matchOrder =
                a.getString(com.android.internal.R.styleable.Transition_matchOrder);
        if (matchOrder != null) {
            transition.setMatchOrder(parseMatchOrder(matchOrder));
        }
        a.recycle();
        return transition;
    }
+9 −0
Original line number Diff line number Diff line
@@ -4973,6 +4973,15 @@
        <attr name="startDelay" format="integer" />
        <!-- Interpolator to be used in the animations spawned by this transition. -->
        <attr name="interpolator" />
        <!-- The match order to use for the transition. This is a comma-separated
             list of values, containing one or more of the following:
             id, itemId, viewName, instance. These correspond to
             {@link android.transition.Transition#MATCH_ID},
             {@link android.transition.Transition#MATCH_ITEM_ID},
             {@link android.transition.Transition#MATCH_VIEW_NAME}, and
             {@link android.transition.Transition#MATCH_INSTANCE}, respectively.
             This corresponds to {@link android.transition.Transition#setMatchOrder(int...)}. -->
        <attr name="matchOrder" format="string" />
    </declare-styleable>

    <!-- Use <code>fade</code>as the root tag of the XML resource that
+1 −0
Original line number Diff line number Diff line
@@ -2172,6 +2172,7 @@
  <public type="attr" name="splitTrack" />
  <public type="attr" name="targetViewName" />
  <public type="attr" name="excludeViewName" />
  <public type="attr" name="matchOrder" />

  <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />