1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.keyguard;
18
19import android.content.Context;
20import android.view.View;
21import android.view.animation.AnimationUtils;
22import android.view.animation.Interpolator;
23
24/**
25 * A class to make nice appear transitions for views in a tabular layout.
26 */
27public class AppearAnimationUtils implements AppearAnimationCreator<View> {
28
29    public static final long DEFAULT_APPEAR_DURATION = 220;
30
31    private final Interpolator mInterpolator;
32    private final float mStartTranslation;
33    private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
34    protected final float mDelayScale;
35    private final long mDuration;
36    protected boolean mScaleTranslationWithRow;
37    protected boolean mAppearing;
38
39    public AppearAnimationUtils(Context ctx) {
40        this(ctx, DEFAULT_APPEAR_DURATION,
41                1.0f, 1.0f,
42                AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
43    }
44
45    public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
46            float delayScaleFactor, Interpolator interpolator) {
47        mInterpolator = interpolator;
48        mStartTranslation = ctx.getResources().getDimensionPixelOffset(
49                R.dimen.appear_y_translation_start) * translationScaleFactor;
50        mDelayScale = delayScaleFactor;
51        mDuration = duration;
52        mScaleTranslationWithRow = false;
53        mAppearing = true;
54    }
55
56    public void startAnimation(View[][] objects, final Runnable finishListener) {
57        startAnimation(objects, finishListener, this);
58    }
59
60    public void startAnimation(View[] objects, final Runnable finishListener) {
61        startAnimation(objects, finishListener, this);
62    }
63
64    public <T> void startAnimation(T[][] objects, final Runnable finishListener,
65            AppearAnimationCreator<T> creator) {
66        AppearAnimationProperties properties = getDelays(objects);
67        startAnimations(properties, objects, finishListener, creator);
68    }
69
70    public <T> void startAnimation(T[] objects, final Runnable finishListener,
71            AppearAnimationCreator<T> creator) {
72        AppearAnimationProperties properties = getDelays(objects);
73        startAnimations(properties, objects, finishListener, creator);
74    }
75
76    private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
77            final Runnable finishListener, AppearAnimationCreator<T> creator) {
78        if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
79            finishListener.run();
80            return;
81        }
82        for (int row = 0; row < properties.delays.length; row++) {
83            long[] columns = properties.delays[row];
84            long delay = columns[0];
85            Runnable endRunnable = null;
86            if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
87                endRunnable = finishListener;
88            }
89            creator.createAnimation(objects[row], delay, mDuration,
90                    mStartTranslation, true /* appearing */, mInterpolator, endRunnable);
91        }
92    }
93
94    private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
95            final Runnable finishListener, AppearAnimationCreator<T> creator) {
96        if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
97            finishListener.run();
98            return;
99        }
100        for (int row = 0; row < properties.delays.length; row++) {
101            long[] columns = properties.delays[row];
102            float translation = mScaleTranslationWithRow
103                    ? (float) (Math.pow((properties.delays.length - row), 2)
104                    / properties.delays.length * mStartTranslation)
105                    : mStartTranslation;
106            for (int col = 0; col < columns.length; col++) {
107                long delay = columns[col];
108                Runnable endRunnable = null;
109                if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
110                    endRunnable = finishListener;
111                }
112                creator.createAnimation(objects[row][col], delay, mDuration,
113                        mAppearing ? translation : -translation,
114                        mAppearing, mInterpolator, endRunnable);
115            }
116        }
117    }
118
119    private <T> AppearAnimationProperties getDelays(T[] items) {
120        long maxDelay = -1;
121        mProperties.maxDelayColIndex = -1;
122        mProperties.maxDelayRowIndex = -1;
123        mProperties.delays = new long[items.length][];
124        for (int row = 0; row < items.length; row++) {
125            mProperties.delays[row] = new long[1];
126            long delay = calculateDelay(row, 0);
127            mProperties.delays[row][0] = delay;
128            if (items[row] != null && delay > maxDelay) {
129                maxDelay = delay;
130                mProperties.maxDelayColIndex = 0;
131                mProperties.maxDelayRowIndex = row;
132            }
133        }
134        return mProperties;
135    }
136
137    private <T> AppearAnimationProperties getDelays(T[][] items) {
138        long maxDelay = -1;
139        mProperties.maxDelayColIndex = -1;
140        mProperties.maxDelayRowIndex = -1;
141        mProperties.delays = new long[items.length][];
142        for (int row = 0; row < items.length; row++) {
143            T[] columns = items[row];
144            mProperties.delays[row] = new long[columns.length];
145            for (int col = 0; col < columns.length; col++) {
146                long delay = calculateDelay(row, col);
147                mProperties.delays[row][col] = delay;
148                if (items[row][col] != null && delay > maxDelay) {
149                    maxDelay = delay;
150                    mProperties.maxDelayColIndex = col;
151                    mProperties.maxDelayRowIndex = row;
152                }
153            }
154        }
155        return mProperties;
156    }
157
158    protected long calculateDelay(int row, int col) {
159        return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
160    }
161
162    public Interpolator getInterpolator() {
163        return mInterpolator;
164    }
165
166    public float getStartTranslation() {
167        return mStartTranslation;
168    }
169
170    @Override
171    public void createAnimation(View view, long delay, long duration, float translationY,
172            boolean appearing, Interpolator interpolator, Runnable endRunnable) {
173        if (view != null) {
174            view.setAlpha(appearing ? 0f : 1.0f);
175            view.setTranslationY(appearing ? translationY : 0);
176            view.animate()
177                    .alpha(appearing ? 1f : 0f)
178                    .translationY(appearing ? 0 : translationY)
179                    .setInterpolator(interpolator)
180                    .setDuration(duration)
181                    .setStartDelay(delay);
182            if (view.hasOverlappingRendering()) {
183                view.animate().withLayer();
184            }
185            if (endRunnable != null) {
186                view.animate().withEndAction(endRunnable);
187            }
188        }
189    }
190
191    public class AppearAnimationProperties {
192        public long[][] delays;
193        public int maxDelayRowIndex;
194        public int maxDelayColIndex;
195    }
196}
197