FileDocCategorySizeDatePackage
MultiPaneChallengeLayout.javaAPI DocAndroid 5.1 API22749Thu Mar 12 22:22:42 GMT 2015com.android.keyguard

MultiPaneChallengeLayout

public class MultiPaneChallengeLayout extends android.view.ViewGroup implements ChallengeLayout

Fields Summary
private static final String
TAG
final int
mOrientation
private boolean
mIsBouncing
public static final int
HORIZONTAL
public static final int
VERTICAL
public static final int
ANIMATE_BOUNCE_DURATION
private KeyguardSecurityContainer
mChallengeView
private android.view.View
mUserSwitcherView
private android.view.View
mScrimView
private OnBouncerStateChangedListener
mBouncerListener
private final android.graphics.Rect
mTempRect
private final android.graphics.Rect
mZeroPadding
private final android.graphics.Rect
mInsets
private final android.util.DisplayMetrics
mDisplayMetrics
private final OnClickListener
mScrimClickListener
Constructors Summary
public MultiPaneChallengeLayout(android.content.Context context)


       
        this(context, null);
    
public MultiPaneChallengeLayout(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, 0);
    
public MultiPaneChallengeLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        super(context, attrs, defStyleAttr);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
        mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_android_orientation,
                HORIZONTAL);
        a.recycle();

        final Resources res = getResources();
        mDisplayMetrics = res.getDisplayMetrics();

        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    
Methods Summary
protected booleancheckLayoutParams(ViewGroup.LayoutParams p)

        return p instanceof LayoutParams;
    
protected ViewGroup.LayoutParamsgenerateDefaultLayoutParams()

        return new LayoutParams();
    
public ViewGroup.LayoutParamsgenerateLayoutParams(android.util.AttributeSet attrs)

        return new LayoutParams(getContext(), attrs, this);
    
protected ViewGroup.LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams p)

        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
                new LayoutParams(p);
    
public intgetBouncerAnimationDuration()

        return ANIMATE_BOUNCE_DURATION;
    
private intgetVirtualHeight(com.android.keyguard.MultiPaneChallengeLayout$LayoutParams lp, int height, int heightUsed)

        int virtualHeight = height;
        final View root = getRootView();
        if (root != null) {
            // This calculation is super dodgy and relies on several assumptions.
            // Specifically that the root of the window will be padded in for insets
            // and that the window is LAYOUT_IN_SCREEN.
            virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop() - mInsets.top;
        }
        if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
            // Always measure the user switcher as if there were no IME insets
            // on the window.
            return virtualHeight - heightUsed;
        } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
            return height;
        }
        return Math.min(virtualHeight - heightUsed, height);
    
public voidhideBouncer()

        if (!mIsBouncing) return;
        mIsBouncing = false;
        if (mScrimView != null) {
            if (mChallengeView != null) {
                mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION);
            }

            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
            anim.setDuration(ANIMATE_BOUNCE_DURATION);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mScrimView.setVisibility(INVISIBLE);
                }
            });
            anim.start();
        }
        if (mBouncerListener != null) {
            mBouncerListener.onBouncerStateChanged(false);
        }
    
public booleanisBouncing()

        return mIsBouncing;
    
public booleanisChallengeOverlapping()

        return false;
    
public booleanisChallengeShowing()

        return true;
    
private voidlayoutWithGravity(int width, int height, android.view.View child, android.graphics.Rect padding, boolean adjustPadding)

        final LayoutParams lp = (LayoutParams) child.getLayoutParams();

        final int heightUsed = padding.top + padding.bottom - getPaddingTop() - getPaddingBottom();
        height = getVirtualHeight(lp, height, heightUsed);

        final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());

        final boolean fixedLayoutSize = lp.centerWithinArea > 0;
        final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL;
        final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL;

        final int adjustedWidth;
        final int adjustedHeight;
        if (fixedLayoutHorizontal) {
            final int paddedWidth = width - padding.left - padding.right;
            adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f);
            adjustedHeight = height;
        } else if (fixedLayoutVertical) {
            final int paddedHeight = height - getPaddingTop() - getPaddingBottom();
            adjustedWidth = width;
            adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f);
        } else {
            adjustedWidth = width;
            adjustedHeight = height;
        }

        final boolean isVertical = Gravity.isVertical(gravity);
        final boolean isHorizontal = Gravity.isHorizontal(gravity);
        final int childWidth = child.getMeasuredWidth();
        final int childHeight = child.getMeasuredHeight();

        int left = padding.left;
        int top = padding.top;
        int right = left + childWidth;
        int bottom = top + childHeight;
        switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
            case Gravity.TOP:
                top = fixedLayoutVertical ?
                        padding.top + (adjustedHeight - childHeight) / 2 : padding.top;
                bottom = top + childHeight;
                if (adjustPadding && isVertical) {
                    padding.top = bottom;
                    padding.bottom += childHeight / 2;
                }
                break;
            case Gravity.BOTTOM:
                bottom = fixedLayoutVertical
                        ? padding.top + height - (adjustedHeight - childHeight) / 2
                        : padding.top + height;
                top = bottom - childHeight;
                if (adjustPadding && isVertical) {
                    padding.bottom = height - top;
                    padding.top += childHeight / 2;
                }
                break;
            case Gravity.CENTER_VERTICAL:
                top = padding.top + (height - childHeight) / 2;
                bottom = top + childHeight;
                break;
        }
        switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
            case Gravity.LEFT:
                left = fixedLayoutHorizontal ?
                        padding.left + (adjustedWidth - childWidth) / 2 : padding.left;
                right = left + childWidth;
                if (adjustPadding && isHorizontal && !isVertical) {
                    padding.left = right;
                    padding.right += childWidth / 2;
                }
                break;
            case Gravity.RIGHT:
                right = fixedLayoutHorizontal
                        ? width - padding.right - (adjustedWidth - childWidth) / 2
                        : width - padding.right;
                left = right - childWidth;
                if (adjustPadding && isHorizontal && !isVertical) {
                    padding.right = width - left;
                    padding.left += childWidth / 2;
                }
                break;
            case Gravity.CENTER_HORIZONTAL:
                final int paddedWidth = width - padding.left - padding.right;
                left = (paddedWidth - childWidth) / 2;
                right = left + childWidth;
                break;
        }
        top += mInsets.top;
        bottom += mInsets.top;
        child.layout(left, top, right, bottom);
    
protected voidonLayout(boolean changed, int l, int t, int r, int b)

        final Rect padding = mTempRect;
        padding.left = getPaddingLeft();
        padding.top = getPaddingTop();
        padding.right = getPaddingRight();
        padding.bottom = getPaddingBottom();
        final int width = r - l;
        final int height = b - t;
        final int insetHeight = height - mInsets.top - mInsets.bottom;

        // Reserve extra space in layout for the user switcher by modifying
        // local padding during this layout pass
        if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) {
            layoutWithGravity(width, insetHeight, mUserSwitcherView, padding, true);
        }

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // We did the user switcher above if we have one.
            if (child == mUserSwitcherView || child.getVisibility() == GONE) continue;

            if (child == mScrimView) {
                child.layout(0, 0, width, height);
                continue;
            } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) {
                layoutWithGravity(width, insetHeight, child, mZeroPadding, false);
                continue;
            }

            layoutWithGravity(width, insetHeight, child, padding, false);
        }
    
protected voidonMeasure(int widthSpec, int heightSpec)

        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
            throw new IllegalArgumentException(
                    "MultiPaneChallengeLayout must be measured with an exact size");
        }

        final int width = MeasureSpec.getSize(widthSpec);
        final int height = MeasureSpec.getSize(heightSpec);
        setMeasuredDimension(width, height);

        final int insetHeight = height - mInsets.top - mInsets.bottom;
        final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY);

        int widthUsed = 0;
        int heightUsed = 0;

        // First pass. Find the challenge view and measure the user switcher,
        // which consumes space in the layout.
        mChallengeView = null;
        mUserSwitcherView = null;
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
                if (mChallengeView != null) {
                    throw new IllegalStateException(
                            "There may only be one child of type challenge");
                }
                if (!(child instanceof KeyguardSecurityContainer)) {
                    throw new IllegalArgumentException(
                            "Challenge must be a KeyguardSecurityContainer");
                }
                mChallengeView = (KeyguardSecurityContainer) child;
            } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
                if (mUserSwitcherView != null) {
                    throw new IllegalStateException(
                            "There may only be one child of type userSwitcher");
                }
                mUserSwitcherView = child;

                if (child.getVisibility() == GONE) continue;

                int adjustedWidthSpec = widthSpec;
                int adjustedHeightSpec = insetHeightSpec;
                if (lp.maxWidth >= 0) {
                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
                            Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY);
                }
                if (lp.maxHeight >= 0) {
                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
                            Math.min(lp.maxHeight, insetHeight), MeasureSpec.EXACTLY);
                }
                // measureChildWithMargins will resolve layout direction for the LayoutParams
                measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);

                // Only subtract out space from one dimension. Favor vertical.
                // Offset by 1.5x to add some balance along the other edge.
                if (Gravity.isVertical(lp.gravity)) {
                    heightUsed += child.getMeasuredHeight() * 1.5f;
                } else if (Gravity.isHorizontal(lp.gravity)) {
                    widthUsed += child.getMeasuredWidth() * 1.5f;
                }
            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
                setScrimView(child);
                child.measure(widthSpec, heightSpec);
            }
        }

        // Second pass. Measure everything that's left.
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER ||
                    lp.childType == LayoutParams.CHILD_TYPE_SCRIM ||
                    child.getVisibility() == GONE) {
                // Don't need to measure GONE children, and the user switcher was already measured.
                continue;
            }

            final int virtualHeight = getVirtualHeight(lp, insetHeight, heightUsed);

            int adjustedWidthSpec;
            int adjustedHeightSpec;
            if (lp.centerWithinArea > 0) {
                if (mOrientation == HORIZONTAL) {
                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
                            (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
                            MeasureSpec.EXACTLY);
                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
                            virtualHeight, MeasureSpec.EXACTLY);
                } else {
                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
                            width - widthUsed, MeasureSpec.EXACTLY);
                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
                            (int) (virtualHeight * lp.centerWithinArea + 0.5f),
                            MeasureSpec.EXACTLY);
                }
            } else {
                adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
                        width - widthUsed, MeasureSpec.EXACTLY);
                adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
                        virtualHeight, MeasureSpec.EXACTLY);
            }
            if (lp.maxWidth >= 0) {
                adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
                        Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)),
                        MeasureSpec.EXACTLY);
            }
            if (lp.maxHeight >= 0) {
                adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
                        Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)),
                        MeasureSpec.EXACTLY);
            }

            measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
        }
    
public voidrequestChildFocus(android.view.View child, android.view.View focused)

        if (mIsBouncing && child != mChallengeView) {
            // Clear out of the bouncer if the user tries to move focus outside of
            // the security challenge view.
            hideBouncer();
        }
        super.requestChildFocus(child, focused);
    
public voidsetInsets(android.graphics.Rect insets)

        mInsets.set(insets);
    
public voidsetOnBouncerStateChangedListener(OnBouncerStateChangedListener listener)

        mBouncerListener = listener;
    
voidsetScrimView(android.view.View scrim)

        if (mScrimView != null) {
            mScrimView.setOnClickListener(null);
        }
        mScrimView = scrim;
        if (mScrimView != null) {
            mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f);
            mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE);
            mScrimView.setFocusable(true);
            mScrimView.setOnClickListener(mScrimClickListener);
        }
    
public voidshowBouncer()

        if (mIsBouncing) return;
        mIsBouncing = true;
        if (mScrimView != null) {
            if (mChallengeView != null) {
                mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION);
            }

            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
            anim.setDuration(ANIMATE_BOUNCE_DURATION);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    mScrimView.setVisibility(VISIBLE);
                }
            });
            anim.start();
        }
        if (mBouncerListener != null) {
            mBouncerListener.onBouncerStateChanged(true);
        }
    
public voidshowChallenge(boolean b)