KeyguardWidgetFrame.java revision 8a7785c7aa2df74203276299e10b0d9056cd0560
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 */
16
17package com.android.internal.policy.impl.keyguard;
18
19import android.animation.Animator;
20import android.animation.ObjectAnimator;
21import android.animation.PropertyValuesHolder;
22import android.appwidget.AppWidgetHostView;
23import android.content.Context;
24import android.content.res.Resources;
25import android.graphics.Canvas;
26import android.graphics.LinearGradient;
27import android.graphics.Paint;
28import android.graphics.PorterDuff;
29import android.graphics.PorterDuffXfermode;
30import android.graphics.Rect;
31import android.graphics.Shader;
32import android.graphics.drawable.Drawable;
33import android.util.AttributeSet;
34import android.view.MotionEvent;
35import android.view.View;
36import android.widget.FrameLayout;
37
38import com.android.internal.R;
39
40public class KeyguardWidgetFrame extends FrameLayout {
41    private final static PorterDuffXfermode sAddBlendMode =
42            new PorterDuffXfermode(PorterDuff.Mode.ADD);
43
44    static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f;
45
46    private int mGradientColor;
47    private LinearGradient mForegroundGradient;
48    private LinearGradient mLeftToRightGradient;
49    private LinearGradient mRightToLeftGradient;
50    private Paint mGradientPaint = new Paint();
51    boolean mLeftToRight = true;
52
53    private float mOverScrollAmount = 0f;
54    private final Rect mForegroundRect = new Rect();
55    private int mForegroundAlpha = 0;
56    private CheckLongPressHelper mLongPressHelper;
57    private Animator mFrameFade;
58    private boolean mIsSmall = false;
59
60    private float mBackgroundAlpha;
61    private float mContentAlpha;
62    private float mBackgroundAlphaMultiplier = 1.0f;
63    private Drawable mBackgroundDrawable;
64    private Rect mBackgroundRect = new Rect();
65    private static int mSmallWidgetHeight;
66
67    // Multiple callers may try and adjust the alpha of the frame. When a caller shows
68    // the outlines, we give that caller control, and nobody else can fade them out.
69    // This prevents animation conflicts.
70    private Object mBgAlphaController;
71
72    public KeyguardWidgetFrame(Context context) {
73        this(context, null, 0);
74    }
75
76    public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
77        this(context, attrs, 0);
78    }
79
80    public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
81        super(context, attrs, defStyle);
82
83        mLongPressHelper = new CheckLongPressHelper(this);
84
85        Resources res = context.getResources();
86        // TODO: this padding should really correspond to the padding embedded in the background
87        // drawable (ie. outlines).
88        int padding = (int) (res.getDisplayMetrics().density * 8);
89        setPadding(padding, padding, padding, padding);
90
91        mBackgroundDrawable = res.getDrawable(R.drawable.kg_bouncer_bg_white);
92        mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient);
93        mGradientPaint.setXfermode(sAddBlendMode);
94    }
95
96    @Override
97    protected void onDetachedFromWindow() {
98        cancelLongPress();
99    }
100
101    public void setMaxChallengeTop(int top) {
102        mSmallWidgetHeight = top - getPaddingTop();
103    }
104
105    @Override
106    public boolean onInterceptTouchEvent(MotionEvent ev) {
107        // Watch for longpress events at this level to make sure
108        // users can always pick up this widget
109        switch (ev.getAction()) {
110            case MotionEvent.ACTION_DOWN:
111                mLongPressHelper.postCheckForLongPress(ev);
112                break;
113            case MotionEvent.ACTION_MOVE:
114                mLongPressHelper.onMove(ev);
115                break;
116            case MotionEvent.ACTION_POINTER_DOWN:
117            case MotionEvent.ACTION_UP:
118            case MotionEvent.ACTION_CANCEL:
119                mLongPressHelper.cancelLongPress();
120                break;
121        }
122
123        // Otherwise continue letting touch events fall through to children
124        return false;
125    }
126
127    @Override
128    public boolean onTouchEvent(MotionEvent ev) {
129        // Watch for longpress events at this level to make sure
130        // users can always pick up this widget
131        switch (ev.getAction()) {
132            case MotionEvent.ACTION_MOVE:
133                mLongPressHelper.onMove(ev);
134                break;
135            case MotionEvent.ACTION_POINTER_DOWN:
136            case MotionEvent.ACTION_UP:
137            case MotionEvent.ACTION_CANCEL:
138                mLongPressHelper.cancelLongPress();
139                break;
140        }
141
142        // We return true here to ensure that we will get cancel / up signal
143        // even if none of our children have requested touch.
144        return true;
145    }
146
147    @Override
148    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
149        super.requestDisallowInterceptTouchEvent(disallowIntercept);
150        cancelLongPress();
151    }
152
153    @Override
154    public void cancelLongPress() {
155        super.cancelLongPress();
156        mLongPressHelper.cancelLongPress();
157    }
158
159
160    private void drawGradientOverlay(Canvas c) {
161        mGradientPaint.setShader(mForegroundGradient);
162        mGradientPaint.setAlpha(mForegroundAlpha);
163        c.drawRect(mForegroundRect, mGradientPaint);
164    }
165
166    protected void drawBg(Canvas canvas) {
167        if (mBackgroundAlpha > 0.0f) {
168            Drawable bg = mBackgroundDrawable;
169
170            bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
171            bg.setBounds(mBackgroundRect);
172            bg.draw(canvas);
173        }
174    }
175
176    @Override
177    protected void dispatchDraw(Canvas canvas) {
178        drawBg(canvas);
179        super.dispatchDraw(canvas);
180        drawGradientOverlay(canvas);
181    }
182
183    /**
184     * Because this view has fading outlines, it is essential that we enable hardware
185     * layers on the content (child) so that updating the alpha of the outlines doesn't
186     * result in the content layer being recreated.
187     */
188    public void enableHardwareLayersForContent() {
189        View widget = getContent();
190        if (widget != null) {
191            widget.setLayerType(LAYER_TYPE_HARDWARE, null);
192        }
193    }
194
195    /**
196     * Because this view has fading outlines, it is essential that we enable hardware
197     * layers on the content (child) so that updating the alpha of the outlines doesn't
198     * result in the content layer being recreated.
199     */
200    public void disableHardwareLayersForContent() {
201        View widget = getContent();
202        if (widget != null) {
203            widget.setLayerType(LAYER_TYPE_NONE, null);
204        }
205    }
206
207    public void enableHardwareLayers() {
208        setLayerType(LAYER_TYPE_HARDWARE, null);
209    }
210
211    public void disableHardwareLayers() {
212        setLayerType(LAYER_TYPE_NONE, null);
213    }
214
215    public View getContent() {
216        return getChildAt(0);
217    }
218
219    public int getContentAppWidgetId() {
220        View content = getContent();
221        if (content instanceof AppWidgetHostView) {
222            return ((AppWidgetHostView) content).getAppWidgetId();
223        } else {
224            return ((KeyguardStatusView) content).getAppWidgetId();
225        }
226    }
227
228    public float getBackgroundAlpha() {
229        return mBackgroundAlpha;
230    }
231
232    public void setBackgroundAlphaMultiplier(float multiplier) {
233        if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) {
234            mBackgroundAlphaMultiplier = multiplier;
235            invalidate();
236        }
237    }
238
239    public float getBackgroundAlphaMultiplier() {
240        return mBackgroundAlphaMultiplier;
241    }
242
243    public void setBackgroundAlpha(float alpha) {
244        if (Float.compare(mBackgroundAlpha, alpha) != 0) {
245            mBackgroundAlpha = alpha;
246            invalidate();
247        }
248    }
249
250    public float getContentAlpha() {
251        return mContentAlpha;
252    }
253
254    public void setContentAlpha(float alpha) {
255        mContentAlpha = alpha;
256        View content = getContent();
257        if (content != null) {
258            content.setAlpha(alpha);
259        }
260    }
261
262    /**
263     * Set the top location of the challenge.
264     *
265     * @param top The top of the challenge, in _local_ coordinates, or -1 to indicate the challenge
266     *              is down.
267     */
268    private void setChallengeTop(int top, boolean updateWidgetSize) {
269        // The widget starts below the padding, and extends to the top of the challengs.
270        int widgetHeight = top - getPaddingTop();
271        int frameHeight = top + getPaddingBottom();
272        setFrameHeight(frameHeight);
273        if (updateWidgetSize) {
274            setWidgetHeight(widgetHeight);
275        }
276    }
277
278    /**
279     * Depending on whether the security is up, the widget size needs to change
280     *
281     * @param height The height of the widget, -1 for full height
282     */
283    private void setWidgetHeight(int height) {
284        boolean needLayout = false;
285        View widget = getContent();
286        if (widget != null) {
287            LayoutParams lp = (LayoutParams) widget.getLayoutParams();
288            if (lp.height != height) {
289                needLayout = true;
290                lp.height = height;
291            }
292        }
293        if (needLayout) {
294            requestLayout();
295        }
296    }
297
298    public boolean isSmall() {
299        return mIsSmall;
300    }
301
302    public void adjustFrame(int challengeTop) {
303        setChallengeTop(challengeTop, false);
304    }
305
306    public void shrinkWidget() {
307        mIsSmall = true;
308        setChallengeTop(mSmallWidgetHeight, true);
309    }
310
311    public void resetSize() {
312        mIsSmall = false;
313        setFrameHeight(getMeasuredHeight());
314        setWidgetHeight(LayoutParams.MATCH_PARENT);
315    }
316
317    public void setFrameHeight(int height) {
318        height = Math.min(height, getMeasuredHeight());
319        mBackgroundRect.set(0, 0, getMeasuredWidth(), height);
320        invalidate();
321    }
322
323    public void hideFrame(Object caller) {
324        fadeFrame(caller, false, 0f, 150);
325    }
326
327    public void showFrame(Object caller) {
328        fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER, 150);
329    }
330
331    public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) {
332        if (takeControl) {
333            mBgAlphaController = caller;
334        }
335
336        if (mBgAlphaController != caller) return;
337
338        if (mFrameFade != null) {
339            mFrameFade.cancel();
340            mFrameFade = null;
341        }
342        PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha);
343        mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha);
344        mFrameFade.setDuration(duration);
345        mFrameFade.start();
346    }
347
348    @Override
349    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
350        super.onSizeChanged(w, h, oldw, oldh);
351        mForegroundRect.set(getPaddingLeft(), getPaddingTop(),
352                w - getPaddingRight(), h - getPaddingBottom());
353        float x0 = mLeftToRight ? 0 : mForegroundRect.width();
354        float x1 = mLeftToRight ? mForegroundRect.width(): 0;
355        mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f,
356                mGradientColor, 0, Shader.TileMode.CLAMP);
357        mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
358                mGradientColor, 0, Shader.TileMode.CLAMP);
359        mBackgroundRect.set(0, 0, w, h);
360        invalidate();
361    }
362
363    void setOverScrollAmount(float r, boolean left) {
364        if (Float.compare(mOverScrollAmount, r) != 0) {
365            mOverScrollAmount = r;
366            mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient;
367            mForegroundAlpha = (int) Math.round((0.85f * r * 255));
368            invalidate();
369        }
370    }
371
372    public void onActive(boolean isActive) {
373        // hook for subclasses
374    }
375
376    public boolean onUserInteraction(int action) {
377        // hook for subclasses
378        return false;
379    }
380
381    public void onChallengeActive(boolean challengeActive) {
382        // hook for subclasses
383    }
384}
385