14ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek/*
24ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * Copyright (C) 2016 The Android Open Source Project
34ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek *
44ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License");
54ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * you may not use this file except in compliance with the License.
64ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * You may obtain a copy of the License at
74ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek *
84ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek *      http://www.apache.org/licenses/LICENSE-2.0
94ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek *
104ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * Unless required by applicable law or agreed to in writing, software
114ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS,
124ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * See the License for the specific language governing permissions and
144ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * limitations under the License
154ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek */
164ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
174ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinekpackage com.android.systemui.statusbar;
184ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
198f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinekimport android.animation.Animator;
208f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinekimport android.animation.AnimatorListenerAdapter;
218f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinekimport android.animation.ValueAnimator;
224ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinekimport android.util.ArrayMap;
2375524417ac507597b19abb2a91389044bec682adAdrian Roosimport android.util.ArraySet;
244ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinekimport android.view.View;
25646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinekimport android.view.ViewGroup;
264ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
278f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinekimport com.android.systemui.Interpolators;
28646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinekimport com.android.systemui.R;
294ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinekimport com.android.systemui.statusbar.notification.TransformState;
308f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinekimport com.android.systemui.statusbar.stack.StackStateAnimator;
314ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
32646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinekimport java.util.Stack;
33646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek
344ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek/**
354ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek * A view that can be transformed to and from.
364ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek */
374ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinekpublic class ViewTransformationHelper implements TransformableView {
38646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek
39646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek    private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
40646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek
414ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
42fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek    private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
438f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    private ValueAnimator mViewTransformationAnimation;
444ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
454ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    public void addTransformedView(int key, View transformedView) {
464ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        mTransformedViews.put(key, transformedView);
474ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
484ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
494ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    public void reset() {
504ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        mTransformedViews.clear();
514ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
524ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
53fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek    public void setCustomTransformation(CustomTransformation transformation, int viewType) {
54fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek        mCustomTransformations.put(viewType, transformation);
55fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek    }
56fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek
574ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    @Override
584ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    public TransformState getCurrentState(int fadingView) {
594ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        View view = mTransformedViews.get(fadingView);
604ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        if (view != null && view.getVisibility() != View.GONE) {
614ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            return TransformState.createFrom(view);
624ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        }
634ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        return null;
644ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
654ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
664ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    @Override
678f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    public void transformTo(final TransformableView notification, final Runnable endRunnable) {
688f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        if (mViewTransformationAnimation != null) {
698f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            mViewTransformationAnimation.cancel();
708f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        }
718f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
728f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
738f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            @Override
748f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
758f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                transformTo(notification, animation.getAnimatedFraction());
768f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            }
778f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        });
788f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
798f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
8051d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek        mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
8151d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            public boolean mCancelled;
828f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
8351d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            @Override
8451d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            public void onAnimationEnd(Animator animation) {
8551d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                if (!mCancelled) {
8651d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                    if (endRunnable != null) {
8751d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                        endRunnable.run();
888f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    }
8951d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                    setVisible(false);
9051d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                } else {
9151d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                    abortTransformations();
928f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                }
9351d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            }
948f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
9551d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            @Override
9651d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            public void onAnimationCancel(Animator animation) {
9751d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek                mCancelled = true;
9851d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek            }
9951d94917c7dc53845701702bfd63f102a3a2dbc8Selim Cinek        });
1008f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.start();
1018f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    }
1028f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
1038f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    @Override
1048f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    public void transformTo(TransformableView notification, float transformationAmount) {
1054ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        for (Integer viewType : mTransformedViews.keySet()) {
1064ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            TransformState ownState = getCurrentState(viewType);
1074ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            if (ownState != null) {
108fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                CustomTransformation customTransformation = mCustomTransformations.get(viewType);
109fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                if (customTransformation != null && customTransformation.transformTo(
1108f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                        ownState, notification, transformationAmount)) {
111fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                    ownState.recycle();
112fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                    continue;
113fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                }
1144ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                TransformState otherState = notification.getCurrentState(viewType);
1154ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                if (otherState != null) {
1168f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    ownState.transformViewTo(otherState, transformationAmount);
1174ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    otherState.recycle();
1184ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                } else {
1194ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    // there's no other view available
1208f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    CrossFadeHelper.fadeOut(mTransformedViews.get(viewType), transformationAmount);
1214ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                }
1224ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                ownState.recycle();
1234ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            }
1244ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        }
1258f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    }
1268f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
1278f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    @Override
1288f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    public void transformFrom(final TransformableView notification) {
1298f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        if (mViewTransformationAnimation != null) {
1308f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            mViewTransformationAnimation.cancel();
1314ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        }
1328f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
1338f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
1348f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            @Override
1358f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
1368f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                transformFrom(notification, animation.getAnimatedFraction());
1378f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            }
1388f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        });
1398f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
1408f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            public boolean mCancelled;
1418f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
1428f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            @Override
1438f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            public void onAnimationEnd(Animator animation) {
1448f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                if (!mCancelled) {
1458f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    setVisible(true);
1468f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                } else {
1478f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    abortTransformations();
1488f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                }
1498f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            }
1508f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
1518f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            @Override
1528f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            public void onAnimationCancel(Animator animation) {
1538f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                mCancelled = true;
1548f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            }
1558f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        });
1568f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
1578f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1588f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        mViewTransformationAnimation.start();
1594ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
1604ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
1614ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    @Override
1628f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    public void transformFrom(TransformableView notification, float transformationAmount) {
1634ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        for (Integer viewType : mTransformedViews.keySet()) {
1644ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            TransformState ownState = getCurrentState(viewType);
1654ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            if (ownState != null) {
166fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                CustomTransformation customTransformation = mCustomTransformations.get(viewType);
167fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                if (customTransformation != null && customTransformation.transformFrom(
1688f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                        ownState, notification, transformationAmount)) {
169fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                    ownState.recycle();
170fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                    continue;
171fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek                }
1724ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                TransformState otherState = notification.getCurrentState(viewType);
1734ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                if (otherState != null) {
1748f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    ownState.transformViewFrom(otherState, transformationAmount);
1754ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    otherState.recycle();
1764ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                } else {
1774ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    // There's no other view, lets fade us in
1784ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    // Certain views need to prepare the fade in and make sure its children are
1794ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                    // completely visible. An example is the notification header.
1808f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    if (transformationAmount == 0.0f) {
1818f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                        ownState.prepareFadeIn();
1828f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    }
1838f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                    CrossFadeHelper.fadeIn(mTransformedViews.get(viewType), transformationAmount);
1844ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                }
1854ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                ownState.recycle();
1864ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            }
1874ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        }
1884ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
1894ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek
1904ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    @Override
1914ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    public void setVisible(boolean visible) {
192d607daf30e8ce3d42cc6896271ab1e7679c7a5faSelim Cinek        if (mViewTransformationAnimation != null) {
193d607daf30e8ce3d42cc6896271ab1e7679c7a5faSelim Cinek            mViewTransformationAnimation.cancel();
194d607daf30e8ce3d42cc6896271ab1e7679c7a5faSelim Cinek        }
1954ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        for (Integer viewType : mTransformedViews.keySet()) {
1964ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            TransformState ownState = getCurrentState(viewType);
1974ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            if (ownState != null) {
19875524417ac507597b19abb2a91389044bec682adAdrian Roos                ownState.setVisible(visible, false /* force */);
1994ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek                ownState.recycle();
2004ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek            }
2014ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek        }
2024ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek    }
203646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek
2048f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    private void abortTransformations() {
2058f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        for (Integer viewType : mTransformedViews.keySet()) {
2068f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            TransformState ownState = getCurrentState(viewType);
2078f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            if (ownState != null) {
2088f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                ownState.abortTransformation();
2098f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                ownState.recycle();
2108f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            }
2118f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        }
2128f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    }
2138f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
214646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek    /**
215646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek     * Add the remaining transformation views such that all views are being transformed correctly
216646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek     * @param viewRoot the root below which all elements need to be transformed
217646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek     */
218646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek    public void addRemainingTransformTypes(View viewRoot) {
219646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        // lets now tag the right views
220646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        int numValues = mTransformedViews.size();
221646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        for (int i = 0; i < numValues; i++) {
222646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            View view = mTransformedViews.valueAt(i);
223646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            while (view != viewRoot.getParent()) {
224646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                view.setTag(TAG_CONTAINS_TRANSFORMED_VIEW, true);
225646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                view = (View) view.getParent();
226646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            }
227646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        }
228646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        Stack<View> stack = new Stack<>();
229646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        // Add the right views now
230646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        stack.push(viewRoot);
231646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        while (!stack.isEmpty()) {
232646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            View child = stack.pop();
233646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            if (child.getVisibility() == View.GONE) {
234646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                continue;
235646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            }
236646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            Boolean containsView = (Boolean) child.getTag(TAG_CONTAINS_TRANSFORMED_VIEW);
237646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            if (containsView == null) {
238646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                // This one is unhandled, let's add it to our list.
239646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                int id = child.getId();
240646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                if (id != View.NO_ID) {
241646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                    // We only fade views with an id
242646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                    addTransformedView(id, child);
243646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                    continue;
244646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                }
245646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            }
246646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            child.setTag(TAG_CONTAINS_TRANSFORMED_VIEW, null);
247646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            if (child instanceof ViewGroup && !mTransformedViews.containsValue(child)){
248646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                ViewGroup group = (ViewGroup) child;
249646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                for (int i = 0; i < group.getChildCount(); i++) {
250646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                    stack.push(group.getChildAt(i));
251646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek                }
252646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek            }
253646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek        }
254646d2054dd76d7213ced8a73f2352f0d63f20043Selim Cinek    }
255fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek
25675524417ac507597b19abb2a91389044bec682adAdrian Roos    public void resetTransformedView(View view) {
25775524417ac507597b19abb2a91389044bec682adAdrian Roos        TransformState state = TransformState.createFrom(view);
25875524417ac507597b19abb2a91389044bec682adAdrian Roos        state.setVisible(true /* visible */, true /* force */);
25975524417ac507597b19abb2a91389044bec682adAdrian Roos        state.recycle();
26075524417ac507597b19abb2a91389044bec682adAdrian Roos    }
26175524417ac507597b19abb2a91389044bec682adAdrian Roos
26275524417ac507597b19abb2a91389044bec682adAdrian Roos    /**
26375524417ac507597b19abb2a91389044bec682adAdrian Roos     * @return a set of all views are being transformed.
26475524417ac507597b19abb2a91389044bec682adAdrian Roos     */
26575524417ac507597b19abb2a91389044bec682adAdrian Roos    public ArraySet<View> getAllTransformingViews() {
26675524417ac507597b19abb2a91389044bec682adAdrian Roos        return new ArraySet<>(mTransformedViews.values());
26775524417ac507597b19abb2a91389044bec682adAdrian Roos    }
26875524417ac507597b19abb2a91389044bec682adAdrian Roos
2698f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek    public static abstract class CustomTransformation {
270fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek        /**
271fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * Transform a state to the given view
272fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @param ownState the state to transform
273fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @param notification the view to transform to
2748f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * @param transformationAmount how much transformation should be done
275fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @return whether a custom transformation is performed
276fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         */
2778f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        public abstract boolean transformTo(TransformState ownState,
2788f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                TransformableView notification,
2798f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                float transformationAmount);
280fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek
281fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek        /**
282fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * Transform to this state from the given view
283fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @param ownState the state to transform to
284fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @param notification the view to transform from
2858f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * @param transformationAmount how much transformation should be done
286fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         * @return whether a custom transformation is performed
287fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek         */
2888f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        public abstract boolean transformFrom(TransformState ownState,
2898f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                TransformableView notification,
2908f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                float transformationAmount);
2918f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
2928f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        /**
2938f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * Perform a custom initialisation before transforming.
2948f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         *
2958f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * @param ownState our own state
2968f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * @param otherState the other state
2978f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         * @return whether a custom initialization is done
2988f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek         */
2998f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        public boolean initTransformation(TransformState ownState,
3008f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                TransformState otherState) {
3018f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            return false;
3028f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        }
3038f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek
3048f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        public boolean customTransformTarget(TransformState ownState,
3058f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek                TransformState otherState) {
3068f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek            return false;
3078f2f6a67fad7aed720e33a6629cb65d368ebfb80Selim Cinek        }
308fd3e2624b55a0988fc129c2fe0e2db4885eb87f9Selim Cinek    }
3094ffd63611a0d516c9988b37e9c06e6f8390c2a2fSelim Cinek}
310