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