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