1/*
2 * Copyright (C) 2012 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 */
16package com.android.keyguard;
17
18import android.animation.Animator;
19import android.animation.AnimatorSet;
20import android.animation.ObjectAnimator;
21import android.animation.PropertyValuesHolder;
22import android.content.Context;
23import android.util.AttributeSet;
24import android.view.View;
25import android.view.animation.AccelerateInterpolator;
26import android.view.animation.DecelerateInterpolator;
27import android.view.animation.Interpolator;
28
29import java.util.ArrayList;
30
31public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
32
33    private float mAdjacentPagesAngle;
34    private static float MAX_SCROLL_PROGRESS = 1.3f;
35    private static float CAMERA_DISTANCE = 10000;
36    protected AnimatorSet mChildrenTransformsAnimator;
37    float[] mTmpTransform = new float[3];
38
39    public KeyguardWidgetCarousel(Context context, AttributeSet attrs) {
40        this(context, attrs, 0);
41    }
42
43    public KeyguardWidgetCarousel(Context context) {
44        this(context, null, 0);
45    }
46
47    public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) {
48        super(context, attrs, defStyle);
49        mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle);
50    }
51
52    protected float getMaxScrollProgress() {
53        return MAX_SCROLL_PROGRESS;
54    }
55
56    public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
57        View child = getChildAt(index);
58        if (child == null) return 0f;
59
60        boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
61        float scrollProgress = getScrollProgress(screenCenter, child, index);
62
63        if (isOverScrollChild(index, scrollProgress)) {
64            return 1.0f;
65        } else if ((showSidePages && inVisibleRange) || index == getNextPage()) {
66            scrollProgress = getBoundedScrollProgress(screenCenter, child, index);
67            float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS);
68            return alpha;
69        } else {
70            return 0f;
71        }
72    }
73
74    public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
75        boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
76        if (inVisibleRange) {
77            return super.getOutlineAlphaForPage(screenCenter, index, showSidePages);
78        } else {
79            return 0f;
80        }
81    }
82
83    private void updatePageAlphaValues(int screenCenter) {
84        if (mChildrenOutlineFadeAnimation != null) {
85            mChildrenOutlineFadeAnimation.cancel();
86            mChildrenOutlineFadeAnimation = null;
87        }
88        boolean showSidePages = mShowingInitialHints || isPageMoving();
89        if (!isReordering(false)) {
90            for (int i = 0; i < getChildCount(); i++) {
91                KeyguardWidgetFrame child = getWidgetPageAt(i);
92                if (child != null) {
93                    float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages);
94                    float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages);
95                    child.setBackgroundAlpha(outlineAlpha);
96                    child.setContentAlpha(contentAlpha);
97                }
98            }
99        }
100    }
101
102    public void showInitialPageHints() {
103        mShowingInitialHints = true;
104        int count = getChildCount();
105        for (int i = 0; i < count; i++) {
106            boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1;
107            KeyguardWidgetFrame child = getWidgetPageAt(i);
108            if (inVisibleRange) {
109                child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
110                child.setContentAlpha(1f);
111            } else {
112                child.setBackgroundAlpha(0f);
113                child.setContentAlpha(0f);
114            }
115        }
116    }
117
118    @Override
119    protected void screenScrolled(int screenCenter) {
120        mScreenCenter = screenCenter;
121        updatePageAlphaValues(screenCenter);
122        if (isReordering(false)) return;
123        for (int i = 0; i < getChildCount(); i++) {
124            KeyguardWidgetFrame v = getWidgetPageAt(i);
125            float scrollProgress = getScrollProgress(screenCenter, v, i);
126            float boundedProgress = getBoundedScrollProgress(screenCenter, v, i);
127            if (v == mDragView || v == null) continue;
128            v.setCameraDistance(CAMERA_DISTANCE);
129
130            if (isOverScrollChild(i, scrollProgress)) {
131                v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
132                v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
133            } else {
134                int width = v.getMeasuredWidth();
135                float pivotX = (width / 2f) + boundedProgress * (width / 2f);
136                float pivotY = v.getMeasuredHeight() / 2;
137                float rotationY = - mAdjacentPagesAngle * boundedProgress;
138                v.setPivotX(pivotX);
139                v.setPivotY(pivotY);
140                v.setRotationY(rotationY);
141                v.setOverScrollAmount(0f, false);
142            }
143            float alpha = v.getAlpha();
144            // If the view has 0 alpha, we set it to be invisible so as to prevent
145            // it from accepting touches
146            if (alpha == 0) {
147                v.setVisibility(INVISIBLE);
148            } else if (v.getVisibility() != VISIBLE) {
149                v.setVisibility(VISIBLE);
150            }
151        }
152    }
153
154    void animatePagesToNeutral() {
155        if (mChildrenTransformsAnimator != null) {
156            mChildrenTransformsAnimator.cancel();
157            mChildrenTransformsAnimator = null;
158        }
159
160        int count = getChildCount();
161        PropertyValuesHolder alpha;
162        PropertyValuesHolder outlineAlpha;
163        PropertyValuesHolder rotationY;
164        ArrayList<Animator> anims = new ArrayList<Animator>();
165
166        for (int i = 0; i < count; i++) {
167            KeyguardWidgetFrame child = getWidgetPageAt(i);
168            boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
169            if (!inVisibleRange) {
170                child.setRotationY(0f);
171            }
172            alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f);
173            outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha",
174                    KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
175            rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f);
176            ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY);
177            child.setVisibility(VISIBLE);
178            if (!inVisibleRange) {
179                a.setInterpolator(mSlowFadeInterpolator);
180            }
181            anims.add(a);
182        }
183
184        int duration = REORDERING_ZOOM_IN_OUT_DURATION;
185        mChildrenTransformsAnimator = new AnimatorSet();
186        mChildrenTransformsAnimator.playTogether(anims);
187
188        mChildrenTransformsAnimator.setDuration(duration);
189        mChildrenTransformsAnimator.start();
190    }
191
192    private void getTransformForPage(int screenCenter, int index, float[] transform) {
193        View child = getChildAt(index);
194        float boundedProgress = getBoundedScrollProgress(screenCenter, child, index);
195        float rotationY = - mAdjacentPagesAngle * boundedProgress;
196        int width = child.getMeasuredWidth();
197        float pivotX = (width / 2f) + boundedProgress * (width / 2f);
198        float pivotY = child.getMeasuredHeight() / 2;
199
200        transform[0] = pivotX;
201        transform[1] = pivotY;
202        transform[2] = rotationY;
203    }
204
205    Interpolator mFastFadeInterpolator = new Interpolator() {
206        Interpolator mInternal = new DecelerateInterpolator(1.5f);
207        float mFactor = 2.5f;
208        @Override
209        public float getInterpolation(float input) {
210            return mInternal.getInterpolation(Math.min(mFactor * input, 1f));
211        }
212    };
213
214    Interpolator mSlowFadeInterpolator = new Interpolator() {
215        Interpolator mInternal = new AccelerateInterpolator(1.5f);
216        float mFactor = 1.3f;
217        @Override
218        public float getInterpolation(float input) {
219            input -= (1 - 1 / mFactor);
220            input = mFactor * Math.max(input, 0f);
221            return mInternal.getInterpolation(input);
222        }
223    };
224
225    void animatePagesToCarousel() {
226        if (mChildrenTransformsAnimator != null) {
227            mChildrenTransformsAnimator.cancel();
228            mChildrenTransformsAnimator = null;
229        }
230
231        int count = getChildCount();
232        PropertyValuesHolder alpha;
233        PropertyValuesHolder outlineAlpha;
234        PropertyValuesHolder rotationY;
235        PropertyValuesHolder pivotX;
236        PropertyValuesHolder pivotY;
237        ArrayList<Animator> anims = new ArrayList<Animator>();
238
239        for (int i = 0; i < count; i++) {
240            KeyguardWidgetFrame child = getWidgetPageAt(i);
241            float finalAlpha = getAlphaForPage(mScreenCenter, i, true);
242            float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true);
243            getTransformForPage(mScreenCenter, i, mTmpTransform);
244
245            boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
246
247            ObjectAnimator a;
248            alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha);
249            outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha);
250            pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]);
251            pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]);
252            rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]);
253
254            if (inVisibleRange) {
255                // for the central pages we animate into a rotated state
256                a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha,
257                        pivotX, pivotY, rotationY);
258            } else {
259                a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha);
260                a.setInterpolator(mFastFadeInterpolator);
261            }
262            anims.add(a);
263        }
264
265        int duration = REORDERING_ZOOM_IN_OUT_DURATION;
266        mChildrenTransformsAnimator = new AnimatorSet();
267        mChildrenTransformsAnimator.playTogether(anims);
268
269        mChildrenTransformsAnimator.setDuration(duration);
270        mChildrenTransformsAnimator.start();
271    }
272
273    protected void reorderStarting() {
274        mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
275        animatePagesToNeutral();
276    }
277
278    protected boolean zoomIn(final Runnable onCompleteRunnable) {
279        animatePagesToCarousel();
280        return super.zoomIn(onCompleteRunnable);
281    }
282
283    @Override
284    protected void onEndReordering() {
285        super.onEndReordering();
286        mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION);
287    }
288}
289