1254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu/* 2254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Copyright (C) 2015 The Android Open Source Project 3254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * 4254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * in compliance with the License. You may obtain a copy of the License at 6254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * 7254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * http://www.apache.org/licenses/LICENSE-2.0 8254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * 9254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Unless required by applicable law or agreed to in writing, software distributed under the License 10254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * or implied. See the License for the specific language governing permissions and limitations under 12254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * the License. 13254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 14254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gupackage android.support.v17.leanback.widget; 15254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 16254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.content.Context; 17254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.content.res.Resources; 18254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.graphics.drawable.ColorDrawable; 19254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.graphics.drawable.Drawable; 20254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.support.v17.leanback.R; 21254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.support.v17.leanback.system.Settings; 22254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.view.ViewGroup; 23254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Guimport android.view.View; 24254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 25254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 26254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu/** 2785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * ShadowOverlayHelper is a helper class for shadow, overlay color and rounded corner. 2885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * There are many choices to implement Shadow, overlay color. 2985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Initialize it with ShadowOverlayHelper.Builder and it decides the best strategy based 3085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * on options user choose and current platform version. 3185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 324d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * <li> For shadow: it may use 9-patch with opticalBounds or Z-value based shadow for 334d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * API >= 21. When 9-patch is used, it requires a ShadowOverlayContainer 3485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * to include 9-patch views. 354d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * <li> For overlay: it may use ShadowOverlayContainer which overrides draw() or it may 364d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * use setForeground(new ColorDrawable()) for API>=23. The foreground support 374d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * might be disabled if rounded corner is applied due to performance reason. 384d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * <li> For rounded-corner: it uses a ViewOutlineProvider for API>=21. 3985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 4085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * There are two different strategies: use Wrapper with a ShadowOverlayContainer; 4185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * or apply rounded corner, overlay and rounded-corner to the view itself. Below is an example 4285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * of how helper is used. 4385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 4485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * <code> 4585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * ShadowOverlayHelper mHelper = new ShadowOverlayHelper.Builder(). 4685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * .needsOverlay(true).needsRoundedCorner(true).needsShadow(true) 4785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * .build(); 4885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * mHelper.prepareParentForShadow(parentView); // apply optical-bounds for 9-patch shadow. 4985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * mHelper.setOverlayColor(view, Color.argb(0x80, 0x80, 0x80, 0x80)); 5085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * mHelper.setShadowFocusLevel(view, 1.0f); 5185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * ... 5285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * View initializeView(View view) { 5385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * if (mHelper.needsWrapper()) { 5485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * ShadowOverlayContainer wrapper = mHelper.createShadowOverlayContainer(context); 5585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * wrapper.wrap(view); 5685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * return wrapper; 5785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * } else { 5885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * mHelper.onViewCreated(view); 5985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * return view; 6085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * } 6185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * } 6285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * ... 6385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 6485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * </code> 65254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 66254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gupublic final class ShadowOverlayHelper { 67254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 68254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 6985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Builder for creating ShadowOverlayHelper. 7085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 7185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public static final class Builder { 7285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 7385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private boolean needsOverlay; 7485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private boolean needsRoundedCorner; 7585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private boolean needsShadow; 7685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private boolean preferZOrder = true; 7785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private boolean keepForegroundDrawable; 7885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private Options options = Options.DEFAULT; 7985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 8085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 8185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set if needs overlay color. 8285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param needsOverlay True if needs overlay. 8385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Builder object itself. 8485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 8585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder needsOverlay(boolean needsOverlay) { 8685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.needsOverlay = needsOverlay; 8785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 8885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 8985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 9085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 9185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set if needs shadow. 9285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param needsShadow True if needs shadow. 9385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Builder object itself. 9485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 9585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder needsShadow(boolean needsShadow) { 9685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.needsShadow = needsShadow; 9785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 9885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 9985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 10085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 10185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set if needs rounded corner. 10285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param needsRoundedCorner True if needs rounded corner. 10385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Builder object itself. 10485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 10585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder needsRoundedCorner(boolean needsRoundedCorner) { 10685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.needsRoundedCorner = needsRoundedCorner; 10785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 10885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 10985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 11085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 11185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set if prefer z-order shadow. On old devices, z-order shadow might be slow, 11285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * set to false to fall back to static 9-patch shadow. Recommend to read 11385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * from system wide Setting value: see {@link Settings}. 11485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 11585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param preferZOrder True if prefer Z shadow. Default is true. 11685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Builder object itself. 11785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 11885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder preferZOrder(boolean preferZOrder) { 11985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.preferZOrder = preferZOrder; 12085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 12185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 12285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 12385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 12485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set if not using foreground drawable for overlay color. For example if 12585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * the view has already assigned a foreground drawable for other purposes. 12685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * When it's true, helper will use a ShadowOverlayContainer for overlay color. 12785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 12885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param keepForegroundDrawable True to keep the original foreground drawable. 12985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Builder object itself. 13085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 13185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder keepForegroundDrawable(boolean keepForegroundDrawable) { 13285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.keepForegroundDrawable = keepForegroundDrawable; 13385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 13485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 13585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 13685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 13785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set option values e.g. Shadow Z value, rounded corner radius. 13885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 13985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param options The Options object to create ShadowOverlayHelper. 14085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 14185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Builder options(Options options) { 14285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.options = options; 14385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 14485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 14585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 14685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 14785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Create ShadowOverlayHelper object 14885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param context The context uses to read Resources settings. 14985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The ShadowOverlayHelper object. 15085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 15185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public ShadowOverlayHelper build(Context context) { 15285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu final ShadowOverlayHelper helper = new ShadowOverlayHelper(); 15385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsOverlay = needsOverlay; 15485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsRoundedCorner = needsRoundedCorner && supportsRoundedCorner(); 15585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsShadow = needsShadow && supportsShadow(); 15685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 15785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (helper.mNeedsRoundedCorner) { 15885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.setupRoundedCornerRadius(options, context); 15985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 16085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 16185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu // figure out shadow type and if we need use wrapper: 16285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (helper.mNeedsShadow) { 163c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas // if static shadow is preferred or dynamic shadow is not supported, 16485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu // use static shadow, otherwise use dynamic shadow. 16585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (!preferZOrder || !supportsDynamicShadow()) { 16685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mShadowType = SHADOW_STATIC; 16785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu // static shadow requires ShadowOverlayContainer to support crossfading 16885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu // of two shadow views. 16985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsWrapper = true; 17085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 17185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mShadowType = SHADOW_DYNAMIC; 17285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.setupDynamicShadowZ(options, context); 17385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable) 17485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu && helper.mNeedsOverlay); 17585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 17685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 17785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mShadowType = SHADOW_NONE; 17885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable) 17985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu && helper.mNeedsOverlay); 18085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 18185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 18285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return helper; 18385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 18485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 18585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 18685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 18785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 18885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Option values for ShadowOverlayContainer. 18985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 19085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public static final class Options { 19185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 19285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 19385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Default Options for values. 19485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 19585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public static final Options DEFAULT = new Options(); 19685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 19785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private int roundedCornerRadius = 0; // 0 for default value 19885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private float dynamicShadowUnfocusedZ = -1; // < 0 for default value 19985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu private float dynamicShadowFocusedZ = -1; // < 0 for default value 20085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 20185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set value of rounded corner radius. 20285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 20385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param roundedCornerRadius Number of pixels of rounded corner radius. 20485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set to 0 to use default settings. 20585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Options object itself. 20685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 20785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Options roundedCornerRadius(int roundedCornerRadius){ 20885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.roundedCornerRadius = roundedCornerRadius; 20985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 21085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 21185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 21285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 21385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set value of focused and unfocused Z value for shadow. 21485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 21585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param unfocusedZ Number of pixels for unfocused Z value. 216c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas * @param focusedZ Number of pixels for focused Z value. 21785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return The Options object itself. 21885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 21985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public Options dynamicShadowZ(float unfocusedZ, float focusedZ){ 22085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.dynamicShadowUnfocusedZ = unfocusedZ; 22185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu this.dynamicShadowFocusedZ = focusedZ; 22285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return this; 22385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 22485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 22585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 22685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Get radius of rounded corner in pixels. 22785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 22885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return Radius of rounded corner in pixels. 22985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 23085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public final int getRoundedCornerRadius() { 23185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return roundedCornerRadius; 23285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 23385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 23485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 23585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Get z value of shadow when a view is not focused. 23685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 23785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return Z value of shadow when a view is not focused. 23885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 23985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public final float getDynamicShadowUnfocusedZ() { 24085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return dynamicShadowUnfocusedZ; 24185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 24285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 24385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 24485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Get z value of shadow when a view is focused. 24585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * 24685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return Z value of shadow when a view is focused. 24785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 24885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public final float getDynamicShadowFocusedZ() { 24985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return dynamicShadowFocusedZ; 25085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 25185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 25285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 25385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 254254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * No shadow. 255254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 256254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static final int SHADOW_NONE = 1; 257254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 258254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 259254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Shadows are fixed. 260254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 261254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static final int SHADOW_STATIC = 2; 262254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 263254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 264254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Shadows depend on the size, shape, and position of the view. 265254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 266254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static final int SHADOW_DYNAMIC = 3; 267254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 268254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu int mShadowType = SHADOW_NONE; 269254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu boolean mNeedsOverlay; 270254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu boolean mNeedsRoundedCorner; 271254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu boolean mNeedsShadow; 272254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu boolean mNeedsWrapper; 273254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 27485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu int mRoundedCornerRadius; 275254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu float mUnfocusedZ; 276254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu float mFocusedZ; 277254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 278254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 279254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Return true if the platform sdk supports shadow. 280254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 281254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static boolean supportsShadow() { 282254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return StaticShadowHelper.getInstance().supportsShadow(); 283254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 284254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 285254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 286254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Returns true if the platform sdk supports dynamic shadows. 287254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 288254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static boolean supportsDynamicShadow() { 289254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return ShadowHelper.getInstance().supportsDynamicShadow(); 290254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 291254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 292254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 293254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Returns true if the platform sdk supports rounded corner through outline. 294254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 295254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static boolean supportsRoundedCorner() { 296254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return RoundedRectHelper.supportsRoundedCorner(); 297254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 298254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 299254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 300254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * Returns true if view.setForeground() is supported. 301254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 302254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public static boolean supportsForeground() { 303254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return ForegroundHelper.supportsForeground(); 304254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 305254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 30685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /* 30785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * hide from external, should be only created by ShadowOverlayHelper.Options. 308254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 30985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu ShadowOverlayHelper() { 310254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 311254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 312254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 313254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container 314254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * before using shadow. Depending on Shadow type, optical bounds might be applied. 315254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 316254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public void prepareParentForShadow(ViewGroup parent) { 317254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (mShadowType == SHADOW_STATIC) { 318254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu StaticShadowHelper.getInstance().prepareParent(parent); 319254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 320254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 321254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 322254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public int getShadowType() { 323254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return mShadowType; 324254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 325254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 326254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public boolean needsOverlay() { 327254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return mNeedsOverlay; 328254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 329254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 330254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public boolean needsRoundedCorner() { 331254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu return mNeedsRoundedCorner; 332254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 333254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 33485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 33585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Returns true if a "wrapper" ShadowOverlayContainer is needed. 33685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * When needsWrapper() is true, call {@link #createShadowOverlayContainer(Context)} 33785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * to create the wrapper. 33885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 33985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public boolean needsWrapper() { 34085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return mNeedsWrapper; 341254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 342254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 34385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 34485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Create ShadowOverlayContainer for this helper. 34585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @param context Context to create view. 34685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * @return ShadowOverlayContainer. 34785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 34885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public ShadowOverlayContainer createShadowOverlayContainer(Context context) { 34985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (!needsWrapper()) { 35085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu throw new IllegalArgumentException(); 35185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 35285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return new ShadowOverlayContainer(context, mShadowType, mNeedsOverlay, 35385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mUnfocusedZ, mFocusedZ, mRoundedCornerRadius); 354254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 355254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 3564d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu /** 35785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set overlay color for view other than ShadowOverlayContainer. 35885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * See also {@link ShadowOverlayContainer#setOverlayColor(int)}. 3594d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu */ 36085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public static void setNoneWrapperOverlayColor(View view, int color) { 3614d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu Drawable d = ForegroundHelper.getInstance().getForeground(view); 3624d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu if (d instanceof ColorDrawable) { 3634d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu ((ColorDrawable) d).setColor(color); 364254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } else { 3654d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu ForegroundHelper.getInstance().setForeground(view, new ColorDrawable(color)); 366254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 367254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 368254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 369254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 37085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Set overlay color for view, it can be a ShadowOverlayContainer if needsWrapper() is true, 37185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * or other view type. 37285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 37385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public void setOverlayColor(View view, int color) { 37485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (needsWrapper()) { 37585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu ((ShadowOverlayContainer) view).setOverlayColor(color); 37685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 37785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu setNoneWrapperOverlayColor(view, color); 37885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 37985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 38085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 38185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 38285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * Must be called when view is created for cases {@link #needsWrapper()} is false. 383254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu * @param view 384254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 385254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu public void onViewCreated(View view) { 38685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (!needsWrapper()) { 387254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (!mNeedsShadow) { 388254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (mNeedsRoundedCorner) { 38985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu RoundedRectHelper.getInstance().setClipToRoundedOutline(view, 39085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu true, mRoundedCornerRadius); 391254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 392254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } else { 393254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (mShadowType == SHADOW_DYNAMIC) { 394254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu Object tag = ShadowHelper.getInstance().addDynamicShadow( 39585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu view, mUnfocusedZ, mFocusedZ, mRoundedCornerRadius); 396254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu view.setTag(R.id.lb_shadow_impl, tag); 397254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 398254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 399254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 400254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 401254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 402254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu /** 403ce4c2014042fe6e4723bab30741039848adcf4beDake Gu * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused. 4044d14fb53ebf304ad989afbc57baa71cfcafa4e7aDake Gu * This is for view other than ShadowOverlayContainer. 40585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu * See also {@link ShadowOverlayContainer#setShadowFocusLevel(float)}. 406254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu */ 40785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public static void setNoneWrapperShadowFocusLevel(View view, float level) { 408c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas setShadowFocusLevel(getNoneWrapperDynamicShadowImpl(view), SHADOW_DYNAMIC, level); 409254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 410254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu 41185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu /** 412ce4c2014042fe6e4723bab30741039848adcf4beDake Gu * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused. 41385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu */ 41485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu public void setShadowFocusLevel(View view, float level) { 41585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (needsWrapper()) { 41685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu ((ShadowOverlayContainer) view).setShadowFocusLevel(level); 41785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 418c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas setShadowFocusLevel(getNoneWrapperDynamicShadowImpl(view), SHADOW_DYNAMIC, level); 41985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 42085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 42185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 42285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu void setupDynamicShadowZ(Options options, Context context) { 42385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (options.getDynamicShadowUnfocusedZ() < 0f) { 42485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu Resources res = context.getResources(); 42585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mFocusedZ = res.getDimension(R.dimen.lb_material_shadow_focused_z); 42685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mUnfocusedZ = res.getDimension(R.dimen.lb_material_shadow_normal_z); 42785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 42885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mFocusedZ = options.getDynamicShadowFocusedZ(); 42985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mUnfocusedZ = options.getDynamicShadowUnfocusedZ(); 43085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 43185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 43285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 43385833087b2288e0f002de6b4ebcbc0564839a217Dake Gu void setupRoundedCornerRadius(Options options, Context context) { 43485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu if (options.getRoundedCornerRadius() == 0) { 43585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu Resources res = context.getResources(); 43685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mRoundedCornerRadius = res.getDimensionPixelSize( 43785833087b2288e0f002de6b4ebcbc0564839a217Dake Gu R.dimen.lb_rounded_rect_corner_radius); 43885833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } else { 43985833087b2288e0f002de6b4ebcbc0564839a217Dake Gu mRoundedCornerRadius = options.getRoundedCornerRadius(); 44085833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 44185833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 44285833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 443c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas static Object getNoneWrapperDynamicShadowImpl(View view) { 44485833087b2288e0f002de6b4ebcbc0564839a217Dake Gu return view.getTag(R.id.lb_shadow_impl); 44585833087b2288e0f002de6b4ebcbc0564839a217Dake Gu } 44685833087b2288e0f002de6b4ebcbc0564839a217Dake Gu 447254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu static void setShadowFocusLevel(Object impl, int shadowType, float level) { 448254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (impl != null) { 449254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu if (level < 0f) { 450254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu level = 0f; 451254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } else if (level > 1f) { 452254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu level = 1f; 453254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 454254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu switch (shadowType) { 455254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu case SHADOW_DYNAMIC: 456254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu ShadowHelper.getInstance().setShadowFocusLevel(impl, level); 457254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu break; 458254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu case SHADOW_STATIC: 459254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu StaticShadowHelper.getInstance().setShadowFocusLevel(impl, level); 460254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu break; 461254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 462254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 463254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu } 464254b417129de2a8c5612826a152f8a26c8f1d0e8Dake Gu} 465