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