/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.keyguard;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
import com.android.internal.widget.LockPatternUtils;
import java.lang.Override;
/**
* Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
* we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
*
*/
public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
private static final String TAG = "KeyguardSecurityViewFlipper";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private Rect mTempRect = new Rect();
public KeyguardSecurityViewFlipper(Context context) {
this(context, null);
}
public KeyguardSecurityViewFlipper(Context context, AttributeSet attr) {
super(context, attr);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
mTempRect.set(0, 0, 0, 0);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
offsetRectIntoDescendantCoords(child, mTempRect);
ev.offsetLocation(mTempRect.left, mTempRect.top);
result = child.dispatchTouchEvent(ev) || result;
ev.offsetLocation(-mTempRect.left, -mTempRect.top);
}
}
return result;
}
KeyguardSecurityView getSecurityView() {
View child = getChildAt(getDisplayedChild());
if (child instanceof KeyguardSecurityView) {
return (KeyguardSecurityView) child;
}
return null;
}
@Override
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.setKeyguardCallback(callback);
}
}
@Override
public void setLockPatternUtils(LockPatternUtils utils) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.setLockPatternUtils(utils);
}
}
@Override
public void reset() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.reset();
}
}
@Override
public void onPause() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.onPause();
}
}
@Override
public void onResume(int reason) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.onResume(reason);
}
}
@Override
public boolean needsInput() {
KeyguardSecurityView ksv = getSecurityView();
return (ksv != null) ? ksv.needsInput() : false;
}
@Override
public KeyguardSecurityCallback getCallback() {
KeyguardSecurityView ksv = getSecurityView();
return (ksv != null) ? ksv.getCallback() : null;
}
@Override
public void showPromptReason(int reason) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.showPromptReason(reason);
}
}
@Override
public void showMessage(String message, int color) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.showMessage(message, color);
}
}
@Override
public void showUsabilityHint() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.showUsabilityHint();
}
}
@Override
public void startAppearAnimation() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
ksv.startAppearAnimation();
}
}
@Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
return ksv.startDisappearAnimation(finishRunnable);
} else {
return false;
}
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : new LayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
if (DEBUG && widthMode != MeasureSpec.AT_MOST) {
Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) +
" should be AT_MOST");
}
if (DEBUG && heightMode != MeasureSpec.AT_MOST) {
Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) +
" should be AT_MOST");
}
final int widthSize = MeasureSpec.getSize(widthSpec);
final int heightSize = MeasureSpec.getSize(heightSpec);
int maxWidth = widthSize;
int maxHeight = heightSize;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
maxWidth = lp.maxWidth;
}
if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) {
maxHeight = lp.maxHeight;
}
}
final int wPadding = getPaddingLeft() + getPaddingRight();
final int hPadding = getPaddingTop() + getPaddingBottom();
maxWidth = Math.max(0, maxWidth - wPadding);
maxHeight = Math.max(0, maxHeight - hPadding);
int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0;
int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width);
final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height);
child.measure(childWidthSpec, childHeightSpec);
width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding));
height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding));
}
setMeasuredDimension(width + wPadding, height + hPadding);
}
private int makeChildMeasureSpec(int maxSize, int childDimen) {
final int mode;
final int size;
switch (childDimen) {
case LayoutParams.WRAP_CONTENT:
mode = MeasureSpec.AT_MOST;
size = maxSize;
break;
case LayoutParams.MATCH_PARENT:
mode = MeasureSpec.EXACTLY;
size = maxSize;
break;
default:
mode = MeasureSpec.EXACTLY;
size = Math.min(maxSize, childDimen);
break;
}
return MeasureSpec.makeMeasureSpec(size, mode);
}
public static class LayoutParams extends FrameLayout.LayoutParams {
@ViewDebug.ExportedProperty(category = "layout")
public int maxWidth;
@ViewDebug.ExportedProperty(category = "layout")
public int maxHeight;
public LayoutParams(ViewGroup.LayoutParams other) {
super(other);
}
public LayoutParams(LayoutParams other) {
super(other);
maxWidth = other.maxWidth;
maxHeight = other.maxHeight;
}
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
final TypedArray a = c.obtainStyledAttributes(attrs,
R.styleable.KeyguardSecurityViewFlipper_Layout, 0, 0);
maxWidth = a.getDimensionPixelSize(
R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxWidth, 0);
maxHeight = a.getDimensionPixelSize(
R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
a.recycle();
}
/** @hide */
@Override
protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
super.encodeProperties(encoder);
encoder.addProperty("layout:maxWidth", maxWidth);
encoder.addProperty("layout:maxHeight", maxHeight);
}
}
}