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 = false;
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    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
163        return p instanceof LayoutParams;
164    }
165
166    @Override
167    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
168        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : new LayoutParams(p);
169    }
170
171    @Override
172    public LayoutParams generateLayoutParams(AttributeSet attrs) {
173        return new LayoutParams(getContext(), attrs);
174    }
175
176    @Override
177    protected void onMeasure(int widthSpec, int heightSpec) {
178        final int widthMode = MeasureSpec.getMode(widthSpec);
179        final int heightMode = MeasureSpec.getMode(heightSpec);
180        if (DEBUG && widthMode != MeasureSpec.AT_MOST) {
181            Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) +
182                    " should be AT_MOST");
183        }
184        if (DEBUG && heightMode != MeasureSpec.AT_MOST) {
185            Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) +
186                    " should be AT_MOST");
187        }
188
189        final int widthSize = MeasureSpec.getSize(widthSpec);
190        final int heightSize = MeasureSpec.getSize(heightSpec);
191        int maxWidth = widthSize;
192        int maxHeight = heightSize;
193        final int count = getChildCount();
194        for (int i = 0; i < count; i++) {
195            final View child = getChildAt(i);
196            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
197
198            if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
199                maxWidth = lp.maxWidth;
200            }
201            if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) {
202                maxHeight = lp.maxHeight;
203            }
204        }
205
206        final int wPadding = getPaddingLeft() + getPaddingRight();
207        final int hPadding = getPaddingTop() + getPaddingBottom();
208        maxWidth -= wPadding;
209        maxHeight -= hPadding;
210
211        int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0;
212        int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0;
213        for (int i = 0; i < count; i++) {
214            final View child = getChildAt(i);
215            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
216
217            final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width);
218            final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height);
219
220            child.measure(childWidthSpec, childHeightSpec);
221
222            width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding));
223            height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding));
224        }
225        setMeasuredDimension(width + wPadding, height + hPadding);
226    }
227
228    private int makeChildMeasureSpec(int maxSize, int childDimen) {
229        final int mode;
230        final int size;
231        switch (childDimen) {
232            case LayoutParams.WRAP_CONTENT:
233                mode = MeasureSpec.AT_MOST;
234                size = maxSize;
235                break;
236            case LayoutParams.MATCH_PARENT:
237                mode = MeasureSpec.EXACTLY;
238                size = maxSize;
239                break;
240            default:
241                mode = MeasureSpec.EXACTLY;
242                size = Math.min(maxSize, childDimen);
243                break;
244        }
245        return MeasureSpec.makeMeasureSpec(size, mode);
246    }
247
248    public static class LayoutParams extends FrameLayout.LayoutParams {
249        @ViewDebug.ExportedProperty(category = "layout")
250        public int maxWidth;
251
252        @ViewDebug.ExportedProperty(category = "layout")
253        public int maxHeight;
254
255        public LayoutParams(ViewGroup.LayoutParams other) {
256            super(other);
257        }
258
259        public LayoutParams(LayoutParams other) {
260            super(other);
261
262            maxWidth = other.maxWidth;
263            maxHeight = other.maxHeight;
264        }
265
266        public LayoutParams(Context c, AttributeSet attrs) {
267            super(c, attrs);
268
269            final TypedArray a = c.obtainStyledAttributes(attrs,
270                    R.styleable.KeyguardSecurityViewFlipper_Layout, 0, 0);
271            maxWidth = a.getDimensionPixelSize(
272                    R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxWidth, 0);
273            maxHeight = a.getDimensionPixelSize(
274                    R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
275            a.recycle();
276        }
277    }
278}
279