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.keyguard;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.Rect;
22import android.util.AttributeSet;
23import android.util.Log;
24import android.view.MotionEvent;
25import android.view.View;
26import android.view.ViewDebug;
27import android.view.ViewGroup;
28import android.view.WindowManager;
29import android.widget.FrameLayout;
30import android.widget.ViewFlipper;
31
32import com.android.internal.widget.LockPatternUtils;
33
34/**
35 * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
36 * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
37 *
38 */
39public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
40    private static final String TAG = "KeyguardSecurityViewFlipper";
41    private static final boolean DEBUG = KeyguardConstants.DEBUG;
42
43    private Rect mTempRect = new Rect();
44
45    public KeyguardSecurityViewFlipper(Context context) {
46        this(context, null);
47    }
48
49    public KeyguardSecurityViewFlipper(Context context, AttributeSet attr) {
50        super(context, attr);
51    }
52
53    @Override
54    public boolean onTouchEvent(MotionEvent ev) {
55        boolean result = super.onTouchEvent(ev);
56        mTempRect.set(0, 0, 0, 0);
57        for (int i = 0; i < getChildCount(); i++) {
58            View child = getChildAt(i);
59            if (child.getVisibility() == View.VISIBLE) {
60                offsetRectIntoDescendantCoords(child, mTempRect);
61                ev.offsetLocation(mTempRect.left, mTempRect.top);
62                result = child.dispatchTouchEvent(ev) || result;
63                ev.offsetLocation(-mTempRect.left, -mTempRect.top);
64            }
65        }
66        return result;
67    }
68
69    KeyguardSecurityView getSecurityView() {
70        View child = getChildAt(getDisplayedChild());
71        if (child instanceof KeyguardSecurityView) {
72            return (KeyguardSecurityView) child;
73        }
74        return null;
75    }
76
77    @Override
78    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
79        KeyguardSecurityView ksv = getSecurityView();
80        if (ksv != null) {
81            ksv.setKeyguardCallback(callback);
82        }
83    }
84
85    @Override
86    public void setLockPatternUtils(LockPatternUtils utils) {
87        KeyguardSecurityView ksv = getSecurityView();
88        if (ksv != null) {
89            ksv.setLockPatternUtils(utils);
90        }
91    }
92
93    @Override
94    public void reset() {
95        KeyguardSecurityView ksv = getSecurityView();
96        if (ksv != null) {
97            ksv.reset();
98        }
99    }
100
101    @Override
102    public void onPause() {
103        KeyguardSecurityView ksv = getSecurityView();
104        if (ksv != null) {
105            ksv.onPause();
106        }
107    }
108
109    @Override
110    public void onResume(int reason) {
111        KeyguardSecurityView ksv = getSecurityView();
112        if (ksv != null) {
113            ksv.onResume(reason);
114        }
115    }
116
117    @Override
118    public boolean needsInput() {
119        KeyguardSecurityView ksv = getSecurityView();
120        return (ksv != null) ? ksv.needsInput() : false;
121    }
122
123    @Override
124    public KeyguardSecurityCallback getCallback() {
125        KeyguardSecurityView ksv = getSecurityView();
126        return (ksv != null) ? ksv.getCallback() : null;
127    }
128
129    @Override
130    public void showUsabilityHint() {
131        KeyguardSecurityView ksv = getSecurityView();
132        if (ksv != null) {
133            ksv.showUsabilityHint();
134        }
135    }
136
137    @Override
138    public void showBouncer(int duration) {
139        KeyguardSecurityView active = getSecurityView();
140        for (int i = 0; i < getChildCount(); i++) {
141            View child = getChildAt(i);
142            if (child instanceof KeyguardSecurityView) {
143                KeyguardSecurityView ksv = (KeyguardSecurityView) child;
144                ksv.showBouncer(ksv == active ? duration : 0);
145            }
146        }
147    }
148
149    @Override
150    public void hideBouncer(int duration) {
151        KeyguardSecurityView active = getSecurityView();
152        for (int i = 0; i < getChildCount(); i++) {
153            View child = getChildAt(i);
154            if (child instanceof KeyguardSecurityView) {
155                KeyguardSecurityView ksv = (KeyguardSecurityView) child;
156                ksv.hideBouncer(ksv == active ? duration : 0);
157            }
158        }
159    }
160
161    @Override
162    public void startAppearAnimation() {
163        KeyguardSecurityView ksv = getSecurityView();
164        if (ksv != null) {
165            ksv.startAppearAnimation();
166        }
167    }
168
169    @Override
170    public boolean startDisappearAnimation(Runnable finishRunnable) {
171        KeyguardSecurityView ksv = getSecurityView();
172        if (ksv != null) {
173            return ksv.startDisappearAnimation(finishRunnable);
174        } else {
175            return false;
176        }
177    }
178
179    @Override
180    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
181        return p instanceof LayoutParams;
182    }
183
184    @Override
185    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
186        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : new LayoutParams(p);
187    }
188
189    @Override
190    public LayoutParams generateLayoutParams(AttributeSet attrs) {
191        return new LayoutParams(getContext(), attrs);
192    }
193
194    @Override
195    protected void onMeasure(int widthSpec, int heightSpec) {
196        final int widthMode = MeasureSpec.getMode(widthSpec);
197        final int heightMode = MeasureSpec.getMode(heightSpec);
198        if (DEBUG && widthMode != MeasureSpec.AT_MOST) {
199            Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) +
200                    " should be AT_MOST");
201        }
202        if (DEBUG && heightMode != MeasureSpec.AT_MOST) {
203            Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) +
204                    " should be AT_MOST");
205        }
206
207        final int widthSize = MeasureSpec.getSize(widthSpec);
208        final int heightSize = MeasureSpec.getSize(heightSpec);
209        int maxWidth = widthSize;
210        int maxHeight = heightSize;
211        final int count = getChildCount();
212        for (int i = 0; i < count; i++) {
213            final View child = getChildAt(i);
214            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
215
216            if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
217                maxWidth = lp.maxWidth;
218            }
219            if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) {
220                maxHeight = lp.maxHeight;
221            }
222        }
223
224        final int wPadding = getPaddingLeft() + getPaddingRight();
225        final int hPadding = getPaddingTop() + getPaddingBottom();
226        maxWidth -= wPadding;
227        maxHeight -= hPadding;
228
229        int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0;
230        int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0;
231        for (int i = 0; i < count; i++) {
232            final View child = getChildAt(i);
233            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
234
235            final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width);
236            final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height);
237
238            child.measure(childWidthSpec, childHeightSpec);
239
240            width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding));
241            height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding));
242        }
243        setMeasuredDimension(width + wPadding, height + hPadding);
244    }
245
246    private int makeChildMeasureSpec(int maxSize, int childDimen) {
247        final int mode;
248        final int size;
249        switch (childDimen) {
250            case LayoutParams.WRAP_CONTENT:
251                mode = MeasureSpec.AT_MOST;
252                size = maxSize;
253                break;
254            case LayoutParams.MATCH_PARENT:
255                mode = MeasureSpec.EXACTLY;
256                size = maxSize;
257                break;
258            default:
259                mode = MeasureSpec.EXACTLY;
260                size = Math.min(maxSize, childDimen);
261                break;
262        }
263        return MeasureSpec.makeMeasureSpec(size, mode);
264    }
265
266    public static class LayoutParams extends FrameLayout.LayoutParams {
267        @ViewDebug.ExportedProperty(category = "layout")
268        public int maxWidth;
269
270        @ViewDebug.ExportedProperty(category = "layout")
271        public int maxHeight;
272
273        public LayoutParams(ViewGroup.LayoutParams other) {
274            super(other);
275        }
276
277        public LayoutParams(LayoutParams other) {
278            super(other);
279
280            maxWidth = other.maxWidth;
281            maxHeight = other.maxHeight;
282        }
283
284        public LayoutParams(Context c, AttributeSet attrs) {
285            super(c, attrs);
286
287            final TypedArray a = c.obtainStyledAttributes(attrs,
288                    R.styleable.KeyguardSecurityViewFlipper_Layout, 0, 0);
289            maxWidth = a.getDimensionPixelSize(
290                    R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxWidth, 0);
291            maxHeight = a.getDimensionPixelSize(
292                    R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
293            a.recycle();
294        }
295    }
296}
297