KeyguardWidgetPager.java revision 66b9fb1662b304d24984af1ac4cc02f2ae8f4cc3
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.TimeInterpolator;
19import android.appwidget.AppWidgetHostView;
20import android.content.Context;
21import android.util.AttributeSet;
22import android.view.Gravity;
23import android.view.View;
24import android.view.animation.AccelerateInterpolator;
25import android.view.animation.DecelerateInterpolator;
26
27import android.widget.FrameLayout;
28
29public class KeyguardWidgetPager extends PagedView {
30    ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
31    private static float CAMERA_DISTANCE = 10000;
32    private static float TRANSITION_SCALE_FACTOR = 0.74f;
33    private static float TRANSITION_PIVOT = 0.65f;
34    private static float TRANSITION_MAX_ROTATION = 30;
35    private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
36    private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
37    private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
38
39    public KeyguardWidgetPager(Context context, AttributeSet attrs) {
40        this(context, attrs, 0);
41    }
42
43    public KeyguardWidgetPager(Context context) {
44        this(null, null, 0);
45    }
46
47    public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
48        super(context, attrs, defStyle);
49    }
50
51    /*
52     * We wrap widgets in a special frame which handles drawing the overscroll foreground.
53     */
54    public void addWidget(AppWidgetHostView widget) {
55        KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
56        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
57                LayoutParams.MATCH_PARENT);
58        lp.gravity = Gravity.CENTER;
59        // The framework adds a default padding to AppWidgetHostView. We don't need this padding
60        // for the Keyguard, so we override it to be 0.
61        widget.setPadding(0,  0, 0, 0);
62        frame.addView(widget, lp);
63        addView(frame);
64    }
65
66    /*
67     * This interpolator emulates the rate at which the perceived scale of an object changes
68     * as its distance from a camera increases. When this interpolator is applied to a scale
69     * animation on a view, it evokes the sense that the object is shrinking due to moving away
70     * from the camera.
71     */
72    static class ZInterpolator implements TimeInterpolator {
73        private float focalLength;
74
75        public ZInterpolator(float foc) {
76            focalLength = foc;
77        }
78
79        public float getInterpolation(float input) {
80            return (1.0f - focalLength / (focalLength + input)) /
81                (1.0f - focalLength / (focalLength + 1.0f));
82        }
83    }
84
85    @Override
86    protected void overScroll(float amount) {
87        acceleratedOverScroll(amount);
88    }
89
90    // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
91    @Override
92    protected void screenScrolled(int screenCenter) {
93        super.screenScrolled(screenCenter);
94
95        for (int i = 0; i < getChildCount(); i++) {
96            View v = getPageAt(i);
97            if (v != null) {
98                float scrollProgress = getScrollProgress(screenCenter, v, i);
99
100                float interpolatedProgress =
101                        mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
102                float scale = (1 - interpolatedProgress) +
103                        interpolatedProgress * TRANSITION_SCALE_FACTOR;
104                float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
105
106                float alpha;
107
108                if (scrollProgress < 0) {
109                    alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
110                        1 - Math.abs(scrollProgress)) : 1.0f;
111                } else {
112                    // On large screens we need to fade the page as it nears its leftmost position
113                    alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
114                }
115
116                v.setCameraDistance(mDensity * CAMERA_DISTANCE);
117                int pageWidth = v.getMeasuredWidth();
118                int pageHeight = v.getMeasuredHeight();
119
120                if (PERFORM_OVERSCROLL_ROTATION) {
121                    if (i == 0 && scrollProgress < 0) {
122                        // Overscroll to the left
123                        v.setPivotX(TRANSITION_PIVOT * pageWidth);
124                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
125                        if (v instanceof KeyguardWidgetFrame) {
126                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
127                                    true);
128                        }
129                        scale = 1.0f;
130                        alpha = 1.0f;
131                        // On the first page, we don't want the page to have any lateral motion
132                        translationX = 0;
133                    } else if (i == getChildCount() - 1 && scrollProgress > 0) {
134                        // Overscroll to the right
135                        v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
136                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
137                        scale = 1.0f;
138                        alpha = 1.0f;
139                        if (v instanceof KeyguardWidgetFrame) {
140                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
141                                    false);
142                        }
143                        // On the last page, we don't want the page to have any lateral motion.
144                        translationX = 0;
145                    } else {
146                        v.setPivotY(pageHeight / 2.0f);
147                        v.setPivotX(pageWidth / 2.0f);
148                        v.setRotationY(0f);
149                        if (v instanceof KeyguardWidgetFrame) {
150                            ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
151                        }
152                    }
153                }
154
155                v.setTranslationX(translationX);
156                v.setScaleX(scale);
157                v.setScaleY(scale);
158                v.setAlpha(alpha);
159
160                // If the view has 0 alpha, we set it to be invisible so as to prevent
161                // it from accepting touches
162                if (alpha == 0) {
163                    v.setVisibility(INVISIBLE);
164                } else if (v.getVisibility() != VISIBLE) {
165                    v.setVisibility(VISIBLE);
166                }
167            }
168        }
169    }
170}
171