1dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu/* 2dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Copyright (C) 2014 The Android Open Source Project 3dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 4dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * in compliance with the License. You may obtain a copy of the License at 6dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 7dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * http://www.apache.org/licenses/LICENSE-2.0 8dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * 9dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Unless required by applicable law or agreed to in writing, software distributed under the License 10dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * or implied. See the License for the specific language governing permissions and limitations under 12dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * the License. 13dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 14dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gupackage android.support.v17.leanback.widget; 15dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 16dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.content.Context; 17dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.support.v17.leanback.R; 18dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.util.AttributeSet; 19dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.LayoutInflater; 20dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.View; 21dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Guimport android.view.ViewGroup; 224c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Guimport android.graphics.Rect; 23dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 24dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu/** 25dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * ShadowOverlayContainer Provides a SDK version independent wrapper container 26dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to take care of shadow and/or color overlay. 27dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 28dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Shadow and color dimmer overlay are both optional. When shadow is used, it's 29dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * user's responsibility to properly call setClipChildren(false) on parent views if 30dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * the shadow can appear outside bounds of parent views. 31dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container 32dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * before using shadow. Depending on sdk version, optical bounds might be applied 33dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to parent. 34dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 35dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 36ca48c80e6b6d90f6362144950c5f20a8f6839e9dBrian Carlstrom * {@link #initialize(boolean, boolean, boolean)} must be first called on the container 37ca48c80e6b6d90f6362144950c5f20a8f6839e9dBrian Carlstrom * to initialize shadows and/or color overlay. Then call {@link #wrap(View)} to insert 38ca48c80e6b6d90f6362144950c5f20a8f6839e9dBrian Carlstrom * wrapped view into container. 39dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 40dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 41dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Call {@link #setShadowFocusLevel(float)} to control shadow alpha. 42dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 43dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * <p> 44f110e403308bae45a8016f462052d8c2dee870cfTim Kilbourn * Call {@link #setOverlayColor(int)} to control overlay color. 45dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * </p> 46dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 4785886809580d605d44a1f45d8bb80742d6ca987eDake Gupublic class ShadowOverlayContainer extends ViewGroup { 48dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 49dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private boolean mInitialized; 50dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private View mColorDimOverlay; 51dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu private Object mShadowImpl; 5285886809580d605d44a1f45d8bb80742d6ca987eDake Gu private View mWrappedView; 534c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu private static final Rect sTempRect = new Rect(); 54dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 55dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context) { 56dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu this(context, null, 0); 57dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 58dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 59dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context, AttributeSet attrs) { 60dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu this(context, attrs, 0); 61dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 62dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 63dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) { 64dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu super(context, attrs, defStyle); 65dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 66dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 67dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 68dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Return true if the platform sdk supports shadow. 69dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 70dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public static boolean supportsShadow() { 71dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu return ShadowHelper.getInstance().supportsShadow(); 72dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 73dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 74dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 75dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container 76dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * before using shadow. Depending on sdk version, optical bounds might be applied 77dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * to parent. 78dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 79dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public static void prepareParentForShadow(ViewGroup parent) { 80dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu ShadowHelper.getInstance().prepareParent(parent); 81dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 82dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 83dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 8414b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout * Initialize shadows, color overlay. 850bc1aae85ac55af50c2debfe711505093fd3626aCraig Stout * @deprecated use {@link #initialize(boolean, boolean, boolean)} instead. 8614b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout */ 8714b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout @Deprecated 8814b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout public void initialize(boolean hasShadow, boolean hasColorDimOverlay) { 8914b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout initialize(hasShadow, hasColorDimOverlay, true); 9014b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout } 9114b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout 9214b5ad0db56bbee8966f3f04ea4a0d6cc93ebcf8Craig Stout /** 934f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout * Initialize shadows, color overlay, and rounded corners. All are optional. 94dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 954f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) { 96dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mInitialized) { 97dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu throw new IllegalStateException(); 98dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 99dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu mInitialized = true; 100dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (hasShadow) { 1014f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout mShadowImpl = ShadowHelper.getInstance().addShadow(this, roundedCorners); 1024f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout } else if (roundedCorners) { 1034f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout RoundedRectHelper.getInstance().setRoundedRectBackground(this, 1044f34a05cdf73b68c3b2eb8678f740ab15225126aCraig Stout android.graphics.Color.TRANSPARENT); 105dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 106dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (hasColorDimOverlay) { 107dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu mColorDimOverlay = LayoutInflater.from(getContext()) 108dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu .inflate(R.layout.lb_card_color_overlay, this, false); 109dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(mColorDimOverlay); 110dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 111dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 112dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 113dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 114dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused. 115dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 116dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void setShadowFocusLevel(float level) { 117dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mShadowImpl != null) { 118dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (level < 0f) { 119dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu level = 0f; 120dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } else if (level > 1f) { 121dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu level = 1f; 122dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 123dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu ShadowHelper.getInstance().setShadowFocusLevel(mShadowImpl, level); 124dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 125dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 126dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 127dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 128dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Set color (with alpha) of the overlay. 129dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 130dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void setOverlayColor(int overlayColor) { 1319240e796bc63422c28f2707840bd99c48573279bDake Gu if (mColorDimOverlay != null) { 1329240e796bc63422c28f2707840bd99c48573279bDake Gu mColorDimOverlay.setBackgroundColor(overlayColor); 1339240e796bc63422c28f2707840bd99c48573279bDake Gu } 134dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 135dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 136dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu /** 137dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu * Inserts view into the wrapper. 138dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu */ 139dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu public void wrap(View view) { 14085886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (!mInitialized || mWrappedView != null) { 141dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu throw new IllegalStateException(); 142dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 143dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu if (mColorDimOverlay != null) { 144dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(view, indexOfChild(mColorDimOverlay)); 145dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } else { 146dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu addView(view); 147dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 14885886809580d605d44a1f45d8bb80742d6ca987eDake Gu mWrappedView = view; 14985886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 15085886809580d605d44a1f45d8bb80742d6ca987eDake Gu 15185886809580d605d44a1f45d8bb80742d6ca987eDake Gu @Override 15285886809580d605d44a1f45d8bb80742d6ca987eDake Gu protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 15385886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (mWrappedView == null) { 15485886809580d605d44a1f45d8bb80742d6ca987eDake Gu throw new IllegalStateException(); 15585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 15685886809580d605d44a1f45d8bb80742d6ca987eDake Gu // padding and child margin are not supported. 15785886809580d605d44a1f45d8bb80742d6ca987eDake Gu // first measure the wrapped view, then measure the shadow view and/or overlay view. 15885886809580d605d44a1f45d8bb80742d6ca987eDake Gu int childWidthMeasureSpec, childHeightMeasureSpec; 15985886809580d605d44a1f45d8bb80742d6ca987eDake Gu LayoutParams lp = mWrappedView.getLayoutParams(); 16085886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.width == LayoutParams.MATCH_PARENT) { 16185886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = MeasureSpec.makeMeasureSpec 16285886809580d605d44a1f45d8bb80742d6ca987eDake Gu (MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); 16385886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 16485886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); 16585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 16685886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.height == LayoutParams.MATCH_PARENT) { 16785886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = MeasureSpec.makeMeasureSpec 16885886809580d605d44a1f45d8bb80742d6ca987eDake Gu (MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY); 16985886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 17085886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); 17185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 17285886809580d605d44a1f45d8bb80742d6ca987eDake Gu mWrappedView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 17385886809580d605d44a1f45d8bb80742d6ca987eDake Gu 17485886809580d605d44a1f45d8bb80742d6ca987eDake Gu int measuredWidth = mWrappedView.getMeasuredWidth(); 17585886809580d605d44a1f45d8bb80742d6ca987eDake Gu int measuredHeight = mWrappedView.getMeasuredHeight(); 17685886809580d605d44a1f45d8bb80742d6ca987eDake Gu 17785886809580d605d44a1f45d8bb80742d6ca987eDake Gu for (int i = 0; i < getChildCount(); i++) { 17885886809580d605d44a1f45d8bb80742d6ca987eDake Gu View child = getChildAt(i); 17985886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (child == mWrappedView) { 18085886809580d605d44a1f45d8bb80742d6ca987eDake Gu continue; 18185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 18285886809580d605d44a1f45d8bb80742d6ca987eDake Gu lp = child.getLayoutParams(); 18385886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.width == LayoutParams.MATCH_PARENT) { 18485886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = MeasureSpec.makeMeasureSpec 18585886809580d605d44a1f45d8bb80742d6ca987eDake Gu (measuredWidth, MeasureSpec.EXACTLY); 18685886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 18785886809580d605d44a1f45d8bb80742d6ca987eDake Gu childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); 18885886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 18985886809580d605d44a1f45d8bb80742d6ca987eDake Gu 19085886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (lp.height == LayoutParams.MATCH_PARENT) { 19185886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = MeasureSpec.makeMeasureSpec 19285886809580d605d44a1f45d8bb80742d6ca987eDake Gu (measuredHeight, MeasureSpec.EXACTLY); 19385886809580d605d44a1f45d8bb80742d6ca987eDake Gu } else { 19485886809580d605d44a1f45d8bb80742d6ca987eDake Gu childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); 19585886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 19685886809580d605d44a1f45d8bb80742d6ca987eDake Gu child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 19785886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 19885886809580d605d44a1f45d8bb80742d6ca987eDake Gu setMeasuredDimension(measuredWidth, measuredHeight); 19985886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 20085886809580d605d44a1f45d8bb80742d6ca987eDake Gu 20185886809580d605d44a1f45d8bb80742d6ca987eDake Gu @Override 20285886809580d605d44a1f45d8bb80742d6ca987eDake Gu protected void onLayout(boolean changed, int l, int t, int r, int b) { 20385886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int count = getChildCount(); 20485886809580d605d44a1f45d8bb80742d6ca987eDake Gu for (int i = 0; i < count; i++) { 20585886809580d605d44a1f45d8bb80742d6ca987eDake Gu final View child = getChildAt(i); 20685886809580d605d44a1f45d8bb80742d6ca987eDake Gu if (child.getVisibility() != GONE) { 20785886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int width = child.getMeasuredWidth(); 20885886809580d605d44a1f45d8bb80742d6ca987eDake Gu final int height = child.getMeasuredHeight(); 20985886809580d605d44a1f45d8bb80742d6ca987eDake Gu child.layout(0, 0, width, height); 21085886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 21185886809580d605d44a1f45d8bb80742d6ca987eDake Gu } 2124c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu if (mWrappedView != null) { 2134c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu sTempRect.left = (int) mWrappedView.getPivotX(); 2144c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu sTempRect.top = (int) mWrappedView.getPivotY(); 2154c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu offsetDescendantRectToMyCoords(mWrappedView, sTempRect); 2164c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu setPivotX(sTempRect.left); 2174c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu setPivotY(sTempRect.top); 2184c94efdb022031d2cbf5e80c8aa3703b01d78f68Dake Gu } 219dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu } 220dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu 221dfd01bbadc107b6b3b2081ddb0236128c425f380Dake Gu} 222