12dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos/*
22dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * Copyright (C) 2014 The Android Open Source Project
32dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos *
42dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * Licensed under the Apache License, Version 2.0 (the "License");
52dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * you may not use this file except in compliance with the License.
62dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * You may obtain a copy of the License at
72dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos *
82dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos *      http://www.apache.org/licenses/LICENSE-2.0
92dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos *
102dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * Unless required by applicable law or agreed to in writing, software
112dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * distributed under the License is distributed on an "AS IS" BASIS,
122dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * See the License for the specific language governing permissions and
142dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * limitations under the License.
152dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos */
162dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos
172dcab18c6c9381122df6e06a93912e53dff69408Paul Soulospackage android.transition;
182dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos
192dcab18c6c9381122df6e06a93912e53dff69408Paul Soulosimport android.animation.Animator;
202dcab18c6c9381122df6e06a93912e53dff69408Paul Soulosimport android.animation.AnimatorSet;
21c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Guimport android.animation.TypeEvaluator;
2279b2781aa763cbd770f690c16af4448139f37061George Mountimport android.graphics.Bitmap;
2379b2781aa763cbd770f690c16af4448139f37061George Mountimport android.graphics.Canvas;
24c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Guimport android.graphics.Matrix;
25017b13a50c1225ca338357dc1315befa38dc3e33Dake Guimport android.graphics.Rect;
2679b2781aa763cbd770f690c16af4448139f37061George Mountimport android.graphics.RectF;
2779b2781aa763cbd770f690c16af4448139f37061George Mountimport android.graphics.drawable.BitmapDrawable;
28017b13a50c1225ca338357dc1315befa38dc3e33Dake Guimport android.graphics.drawable.Drawable;
2979b2781aa763cbd770f690c16af4448139f37061George Mountimport android.view.View;
3079b2781aa763cbd770f690c16af4448139f37061George Mountimport android.view.ViewGroup;
3179b2781aa763cbd770f690c16af4448139f37061George Mountimport android.widget.ImageView;
322dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos
332dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos/**
342dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * Static utility methods for Transitions.
352dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos *
362dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos * @hide
372dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos */
382dcab18c6c9381122df6e06a93912e53dff69408Paul Soulospublic class TransitionUtils {
3979b2781aa763cbd770f690c16af4448139f37061George Mount    private static int MAX_IMAGE_SIZE = (1024 * 1024);
402dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos
412dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos    static Animator mergeAnimators(Animator animator1, Animator animator2) {
422dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos        if (animator1 == null) {
432dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos            return animator2;
442dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos        } else if (animator2 == null) {
452dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos            return animator1;
462dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos        } else {
472dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos            AnimatorSet animatorSet = new AnimatorSet();
482dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos            animatorSet.playTogether(animator1, animator2);
492dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos            return animatorSet;
502dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos        }
512dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos    }
52c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu
53c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount    public static Transition mergeTransitions(Transition... transitions) {
54c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        int count = 0;
55c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        int nonNullIndex = -1;
56c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        for (int i = 0; i < transitions.length; i++) {
57c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            if (transitions[i] != null) {
58c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount                count++;
59c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount                nonNullIndex = i;
60c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            }
61c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        }
62c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount
63c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        if (count == 0) {
64c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            return null;
65c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        }
66c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount
67c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        if (count == 1) {
68c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            return transitions[nonNullIndex];
69c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        }
70c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount
71c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        TransitionSet transitionSet = new TransitionSet();
72c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        for (int i = 0; i < transitions.length; i++) {
73c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            if (transitions[i] != null) {
74c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount                transitionSet.addTransition(transitions[i]);
75c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount            }
76c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        }
77c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount        return transitionSet;
78c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount    }
79c03da0e7a9ef721709d51cf8a2d539a5bd8a320eGeorge Mount
8079b2781aa763cbd770f690c16af4448139f37061George Mount    /**
8179b2781aa763cbd770f690c16af4448139f37061George Mount     * Creates a View using the bitmap copy of <code>view</code>. If <code>view</code> is large,
8279b2781aa763cbd770f690c16af4448139f37061George Mount     * the copy will use a scaled bitmap of the given view.
8379b2781aa763cbd770f690c16af4448139f37061George Mount     *
8479b2781aa763cbd770f690c16af4448139f37061George Mount     * @param sceneRoot The ViewGroup in which the view copy will be displayed.
8579b2781aa763cbd770f690c16af4448139f37061George Mount     * @param view The view to create a copy of.
8679b2781aa763cbd770f690c16af4448139f37061George Mount     * @param parent The parent of view.
8779b2781aa763cbd770f690c16af4448139f37061George Mount     */
8879b2781aa763cbd770f690c16af4448139f37061George Mount    public static View copyViewImage(ViewGroup sceneRoot, View view, View parent) {
8979b2781aa763cbd770f690c16af4448139f37061George Mount        Matrix matrix = new Matrix();
9079b2781aa763cbd770f690c16af4448139f37061George Mount        matrix.setTranslate(-parent.getScrollX(), -parent.getScrollY());
9179b2781aa763cbd770f690c16af4448139f37061George Mount        view.transformMatrixToGlobal(matrix);
9279b2781aa763cbd770f690c16af4448139f37061George Mount        sceneRoot.transformMatrixToLocal(matrix);
9379b2781aa763cbd770f690c16af4448139f37061George Mount        RectF bounds = new RectF(0, 0, view.getWidth(), view.getHeight());
9479b2781aa763cbd770f690c16af4448139f37061George Mount        matrix.mapRect(bounds);
9579b2781aa763cbd770f690c16af4448139f37061George Mount        int left = Math.round(bounds.left);
9679b2781aa763cbd770f690c16af4448139f37061George Mount        int top = Math.round(bounds.top);
9779b2781aa763cbd770f690c16af4448139f37061George Mount        int right = Math.round(bounds.right);
9879b2781aa763cbd770f690c16af4448139f37061George Mount        int bottom = Math.round(bounds.bottom);
9979b2781aa763cbd770f690c16af4448139f37061George Mount
10079b2781aa763cbd770f690c16af4448139f37061George Mount        ImageView copy = new ImageView(view.getContext());
10179b2781aa763cbd770f690c16af4448139f37061George Mount        copy.setScaleType(ImageView.ScaleType.CENTER_CROP);
10279b2781aa763cbd770f690c16af4448139f37061George Mount        Bitmap bitmap = createViewBitmap(view, matrix, bounds);
10379b2781aa763cbd770f690c16af4448139f37061George Mount        if (bitmap != null) {
10479b2781aa763cbd770f690c16af4448139f37061George Mount            copy.setImageBitmap(bitmap);
10579b2781aa763cbd770f690c16af4448139f37061George Mount        }
10679b2781aa763cbd770f690c16af4448139f37061George Mount        int widthSpec = View.MeasureSpec.makeMeasureSpec(right - left, View.MeasureSpec.EXACTLY);
10779b2781aa763cbd770f690c16af4448139f37061George Mount        int heightSpec = View.MeasureSpec.makeMeasureSpec(bottom - top, View.MeasureSpec.EXACTLY);
10879b2781aa763cbd770f690c16af4448139f37061George Mount        copy.measure(widthSpec, heightSpec);
10979b2781aa763cbd770f690c16af4448139f37061George Mount        copy.layout(left, top, right, bottom);
11079b2781aa763cbd770f690c16af4448139f37061George Mount        return copy;
11179b2781aa763cbd770f690c16af4448139f37061George Mount    }
11279b2781aa763cbd770f690c16af4448139f37061George Mount
11379b2781aa763cbd770f690c16af4448139f37061George Mount    /**
114017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu     * Get a copy of bitmap of given drawable, return null if intrinsic size is zero
115017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu     */
116017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu    public static Bitmap createDrawableBitmap(Drawable drawable) {
117017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int width = drawable.getIntrinsicWidth();
118017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int height = drawable.getIntrinsicHeight();
119017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        if (width <= 0 || height <= 0) {
120017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu            return null;
121017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        }
122017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (width * height));
123017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        if (drawable instanceof BitmapDrawable && scale == 1f) {
124017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu            // return same bitmap if scale down not needed
125017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu            return ((BitmapDrawable) drawable).getBitmap();
126017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        }
127017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int bitmapWidth = (int) (width * scale);
128017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int bitmapHeight = (int) (height * scale);
129017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
130017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        Canvas canvas = new Canvas(bitmap);
131017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        Rect existingBounds = drawable.getBounds();
132017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int left = existingBounds.left;
133017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int top = existingBounds.top;
134017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int right = existingBounds.right;
135017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        int bottom = existingBounds.bottom;
136017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
137017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        drawable.draw(canvas);
138017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        drawable.setBounds(left, top, right, bottom);
139017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu        return bitmap;
140017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu    }
141017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu
142017b13a50c1225ca338357dc1315befa38dc3e33Dake Gu    /**
14379b2781aa763cbd770f690c16af4448139f37061George Mount     * Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
14479b2781aa763cbd770f690c16af4448139f37061George Mount     * coordinates. <code>matrix</code> will be modified during the bitmap creation.
14579b2781aa763cbd770f690c16af4448139f37061George Mount     *
14679b2781aa763cbd770f690c16af4448139f37061George Mount     * <p>If the bitmap is large, it will be scaled uniformly down to at most 1MB size.</p>
14779b2781aa763cbd770f690c16af4448139f37061George Mount     * @param view The view to create a bitmap for.
14879b2781aa763cbd770f690c16af4448139f37061George Mount     * @param matrix The matrix converting the view local coordinates to the coordinates that
14979b2781aa763cbd770f690c16af4448139f37061George Mount     *               the bitmap will be displayed in. <code>matrix</code> will be modified before
15079b2781aa763cbd770f690c16af4448139f37061George Mount     *               returning.
15179b2781aa763cbd770f690c16af4448139f37061George Mount     * @param bounds The bounds of the bitmap in the destination coordinate system (where the
15279b2781aa763cbd770f690c16af4448139f37061George Mount     *               view should be presented. Typically, this is matrix.mapRect(viewBounds);
15379b2781aa763cbd770f690c16af4448139f37061George Mount     * @return A bitmap of the given view or null if bounds has no width or height.
15479b2781aa763cbd770f690c16af4448139f37061George Mount     */
15579b2781aa763cbd770f690c16af4448139f37061George Mount    public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
15679b2781aa763cbd770f690c16af4448139f37061George Mount        Bitmap bitmap = null;
15779b2781aa763cbd770f690c16af4448139f37061George Mount        int bitmapWidth = Math.round(bounds.width());
15879b2781aa763cbd770f690c16af4448139f37061George Mount        int bitmapHeight = Math.round(bounds.height());
15979b2781aa763cbd770f690c16af4448139f37061George Mount        if (bitmapWidth > 0 && bitmapHeight > 0) {
16079b2781aa763cbd770f690c16af4448139f37061George Mount            float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
16179b2781aa763cbd770f690c16af4448139f37061George Mount            bitmapWidth *= scale;
16279b2781aa763cbd770f690c16af4448139f37061George Mount            bitmapHeight *= scale;
16379b2781aa763cbd770f690c16af4448139f37061George Mount            matrix.postTranslate(-bounds.left, -bounds.top);
16479b2781aa763cbd770f690c16af4448139f37061George Mount            matrix.postScale(scale, scale);
16579b2781aa763cbd770f690c16af4448139f37061George Mount            bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
16679b2781aa763cbd770f690c16af4448139f37061George Mount            Canvas canvas = new Canvas(bitmap);
16779b2781aa763cbd770f690c16af4448139f37061George Mount            canvas.concat(matrix);
16879b2781aa763cbd770f690c16af4448139f37061George Mount            view.draw(canvas);
16979b2781aa763cbd770f690c16af4448139f37061George Mount        }
17079b2781aa763cbd770f690c16af4448139f37061George Mount        return bitmap;
17179b2781aa763cbd770f690c16af4448139f37061George Mount    }
17279b2781aa763cbd770f690c16af4448139f37061George Mount
173c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu    public static class MatrixEvaluator implements TypeEvaluator<Matrix> {
174c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu
175c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        float[] mTempStartValues = new float[9];
176c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu
177c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        float[] mTempEndValues = new float[9];
178c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu
179c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        Matrix mTempMatrix = new Matrix();
180c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu
181c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        @Override
182c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
183c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            startValue.getValues(mTempStartValues);
184c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            endValue.getValues(mTempEndValues);
185c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            for (int i = 0; i < 9; i++) {
186c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu                float diff = mTempEndValues[i] - mTempStartValues[i];
187c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu                mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
188c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            }
189c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            mTempMatrix.setValues(mTempEndValues);
190c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu            return mTempMatrix;
191c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu        }
192c94e2b393f6eba684ee2c84eaa50746fc1459d0fDake Gu    }
1932dcab18c6c9381122df6e06a93912e53dff69408Paul Soulos}
194