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