KeyguardWidgetPager.java revision 838906b165e4d3cb2c512b2db344aa50cb5d4751
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.MotionEvent;
24import android.view.View;
25import android.view.ViewGroup;
26import android.view.animation.AccelerateInterpolator;
27import android.view.animation.DecelerateInterpolator;
28
29import android.widget.FrameLayout;
30
31import com.android.internal.R;
32
33public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener {
34    ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
35    private static float CAMERA_DISTANCE = 10000;
36    private static float TRANSITION_SCALE_FACTOR = 0.74f;
37    private static float TRANSITION_PIVOT = 0.65f;
38    private static float TRANSITION_MAX_ROTATION = 30;
39    private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
40    private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
41    private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
42
43    private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
44    private static final boolean CAFETERIA_TRAY = false;
45
46    private int mPage = 0;
47    private Callbacks mCallbacks;
48
49    public KeyguardWidgetPager(Context context, AttributeSet attrs) {
50        this(context, attrs, 0);
51    }
52
53    public KeyguardWidgetPager(Context context) {
54        this(null, null, 0);
55    }
56
57    public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
58        super(context, attrs, defStyle);
59        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
60            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
61        }
62
63        setPageSwitchListener(this);
64    }
65
66    @Override
67    public void onPageSwitch(View newPage, int newPageIndex) {
68        boolean showingStatusWidget = false;
69        if (newPage instanceof ViewGroup) {
70            ViewGroup vg = (ViewGroup) newPage;
71            if (vg.getChildAt(0) instanceof KeyguardStatusView) {
72                showingStatusWidget = true;
73            }
74        }
75
76        // Disable the status bar clock if we're showing the default status widget
77        if (showingStatusWidget) {
78            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
79        } else {
80            setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
81        }
82
83        // Extend the display timeout if the user switches pages
84        if (mPage != newPageIndex) {
85            mPage = newPageIndex;
86            if (mCallbacks != null) {
87                mCallbacks.onUserActivityTimeoutChanged();
88                mCallbacks.userActivity();
89            }
90        }
91    }
92
93    public void showPagingFeedback() {
94        // Nothing yet.
95    }
96
97    public long getUserActivityTimeout() {
98        View page = getPageAt(mPage);
99        if (page instanceof ViewGroup) {
100            ViewGroup vg = (ViewGroup) page;
101            View view = vg.getChildAt(0);
102            if (!(view instanceof KeyguardStatusView)
103                    && !(view instanceof KeyguardMultiUserSelectorView)) {
104                return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
105            }
106        }
107        return -1;
108    }
109
110    public void setCallbacks(Callbacks callbacks) {
111        mCallbacks = callbacks;
112    }
113
114    public interface Callbacks {
115        public void userActivity();
116        public void onUserActivityTimeoutChanged();
117    }
118
119    /*
120     * We wrap widgets in a special frame which handles drawing the overscroll foreground.
121     */
122    public void addWidget(AppWidgetHostView widget) {
123        KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
124        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
125                LayoutParams.MATCH_PARENT);
126        lp.gravity = Gravity.CENTER;
127        // The framework adds a default padding to AppWidgetHostView. We don't need this padding
128        // for the Keyguard, so we override it to be 0.
129        widget.setPadding(0,  0, 0, 0);
130        widget.setContentDescription(widget.getAppWidgetInfo().label);
131        frame.addView(widget, lp);
132        addView(frame);
133    }
134
135    protected void onUnhandledTap(MotionEvent ev) {
136        showPagingFeedback();
137    }
138
139    @Override
140    protected void onPageBeginMoving() {
141        // Enable hardware layers while pages are moving
142        // TODO: We should only do this for the two views that are actually moving
143        int children = getChildCount();
144        for (int i = 0; i < children; i++) {
145            getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null);
146        }
147    }
148
149    @Override
150    protected void onPageEndMoving() {
151        // Disable hardware layers while pages are moving
152        int children = getChildCount();
153        for (int i = 0; i < children; i++) {
154            getChildAt(i).setLayerType(LAYER_TYPE_NONE, null);
155        }
156    }
157
158    /*
159     * This interpolator emulates the rate at which the perceived scale of an object changes
160     * as its distance from a camera increases. When this interpolator is applied to a scale
161     * animation on a view, it evokes the sense that the object is shrinking due to moving away
162     * from the camera.
163     */
164    static class ZInterpolator implements TimeInterpolator {
165        private float focalLength;
166
167        public ZInterpolator(float foc) {
168            focalLength = foc;
169        }
170
171        public float getInterpolation(float input) {
172            return (1.0f - focalLength / (focalLength + input)) /
173                (1.0f - focalLength / (focalLength + 1.0f));
174        }
175    }
176
177    @Override
178    public String getCurrentPageDescription() {
179        final int nextPageIndex = getNextPage();
180        if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) {
181            KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex);
182            CharSequence title = frame.getChildAt(0).getContentDescription();
183            if (title == null) {
184                title = "";
185            }
186            return mContext.getString(
187                    com.android.internal.R.string.keyguard_accessibility_widget_changed,
188                    title, nextPageIndex + 1, getChildCount());
189        }
190        return super.getCurrentPageDescription();
191    }
192
193    @Override
194    protected void overScroll(float amount) {
195        acceleratedOverScroll(amount);
196    }
197
198    // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
199    @Override
200    protected void screenScrolled(int screenCenter) {
201        super.screenScrolled(screenCenter);
202
203        for (int i = 0; i < getChildCount(); i++) {
204            View v = getPageAt(i);
205            if (v != null) {
206                float scrollProgress = getScrollProgress(screenCenter, v, i);
207                float interpolatedProgress =
208                        mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
209                float scale = 1.0f;
210                float translationX = 0;
211                float alpha = 1.0f;
212
213                if (CAFETERIA_TRAY) {
214                    scale = (1 - interpolatedProgress) +
215                            interpolatedProgress * TRANSITION_SCALE_FACTOR;
216                    translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
217
218                    if (scrollProgress < 0) {
219                        alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
220                            1 - Math.abs(scrollProgress)) : 1.0f;
221                    } else {
222                        // On large screens we need to fade the page as it nears its leftmost position
223                        alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
224                    }
225                }
226
227                v.setCameraDistance(mDensity * CAMERA_DISTANCE);
228                int pageWidth = v.getMeasuredWidth();
229                int pageHeight = v.getMeasuredHeight();
230
231                if (PERFORM_OVERSCROLL_ROTATION) {
232                    if (i == 0 && scrollProgress < 0) {
233                        // Overscroll to the left
234                        v.setPivotX(TRANSITION_PIVOT * pageWidth);
235                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
236                        if (v instanceof KeyguardWidgetFrame) {
237                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
238                                    true);
239                        }
240                        scale = 1.0f;
241                        alpha = 1.0f;
242                        // On the first page, we don't want the page to have any lateral motion
243                        translationX = 0;
244                    } else if (i == getChildCount() - 1 && scrollProgress > 0) {
245                        // Overscroll to the right
246                        v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
247                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
248                        scale = 1.0f;
249                        alpha = 1.0f;
250                        if (v instanceof KeyguardWidgetFrame) {
251                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
252                                    false);
253                        }
254                        // On the last page, we don't want the page to have any lateral motion.
255                        translationX = 0;
256                    } else {
257                        v.setPivotY(pageHeight / 2.0f);
258                        v.setPivotX(pageWidth / 2.0f);
259                        v.setRotationY(0f);
260                        if (v instanceof KeyguardWidgetFrame) {
261                            ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
262                        }
263                    }
264                }
265
266                v.setTranslationX(translationX);
267                v.setScaleX(scale);
268                v.setScaleY(scale);
269                v.setAlpha(alpha);
270
271                // If the view has 0 alpha, we set it to be invisible so as to prevent
272                // it from accepting touches
273                if (alpha == 0) {
274                    v.setVisibility(INVISIBLE);
275                } else if (v.getVisibility() != VISIBLE) {
276                    v.setVisibility(VISIBLE);
277                }
278            }
279        }
280    }
281}
282