GradientColor.java revision 0b9295d06750dc6da032a2b2092e2c500c65393f
1dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu/* 2dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Copyright (C) 2016 The Android Open Source Project 3dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 4dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Licensed under the Apache License, Version 2.0 (the "License"); 5dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * you may not use this file except in compliance with the License. 6dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * You may obtain a copy of the License at 7dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 8dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * http://www.apache.org/licenses/LICENSE-2.0 9dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 10dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Unless required by applicable law or agreed to in writing, software 11dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * distributed under the License is distributed on an "AS IS" BASIS, 12dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * See the License for the specific language governing permissions and 14dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * limitations under the License. 15dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 16dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 17dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhupackage android.content.res; 18dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 19dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.annotation.ColorInt; 201664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhuimport android.annotation.IntDef; 21dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.annotation.NonNull; 22dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.annotation.Nullable; 23ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.content.pm.ActivityInfo.Config; 24dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.content.res.Resources.Theme; 25dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 26dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport com.android.internal.R; 27dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport com.android.internal.util.GrowingArrayUtils; 28dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 29dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport org.xmlpull.v1.XmlPullParser; 30dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport org.xmlpull.v1.XmlPullParserException; 31dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 32dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.graphics.LinearGradient; 33dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.graphics.RadialGradient; 34dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.graphics.Shader; 35dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.graphics.SweepGradient; 36dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.graphics.drawable.GradientDrawable; 37dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.util.AttributeSet; 38dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.util.Log; 39dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport android.util.Xml; 40dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 41dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhuimport java.io.IOException; 421664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhuimport java.lang.annotation.Retention; 431664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhuimport java.lang.annotation.RetentionPolicy; 44dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 451664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu/** 461664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * Lets you define a gradient color, which is used inside 471664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * {@link android.graphics.drawable.VectorDrawable}. 481664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * 491664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * {@link android.content.res.GradientColor}s are created from XML resource files defined in the 501664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * "color" subdirectory directory of an application's resource directory. The XML file contains 511664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * a single "gradient" element with a number of attributes and elements inside. For example: 521664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <pre> 531664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <gradient xmlns:android="http://schemas.android.com/apk/res/android"> 541664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <android:startColor="?android:attr/colorPrimary"/> 551664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <android:endColor="?android:attr/colorControlActivated"/> 561664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <.../> 571664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * <android:type="linear"/> 581664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * </gradient> 591664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * </pre> 601664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * 611664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * This can describe either a {@link android.graphics.LinearGradient}, 621664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}. 631664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * 641664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * Note that different attributes are relevant for different types of gradient. 651664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * For example, android:gradientRadius is only applied to RadialGradient. 661664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * androd:centerX and android:centerY are only applied to SweepGradient or RadialGradient. 671664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * android:startX, android:startY, android:endX and android:endY are only applied to LinearGradient. 681664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * 691664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * Also note if any color "item" element is defined, then startColor, centerColor and endColor will 701664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu * be ignored. 711664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu */ 72dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhupublic class GradientColor extends ComplexColor { 73dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private static final String TAG = "GradientColor"; 74dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 75dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private static final boolean DBG_GRADIENT = false; 76dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 771664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR}) 781664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu @Retention(RetentionPolicy.SOURCE) 791664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private @interface GradientTileMode {} 801664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private static final int TILE_MODE_CLAMP = 0; 811664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private static final int TILE_MODE_REPEAT = 1; 821664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private static final int TILE_MODE_MIRROR = 2; 831664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu 84dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** Lazily-created factory for this GradientColor. */ 85dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private GradientColorFactory mFactory; 86dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 87ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette private @Config int mChangingConfigurations; 88dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int mDefaultColor; 89dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 90dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // After parsing all the attributes from XML, this shader is the ultimate result containing 91dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // all the XML information. 92dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private Shader mShader = null; 93dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 941664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu // Below are the attributes at the root element <gradient>. 951664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu // NOTE: they need to be copied in the copy constructor! 96dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int mGradientType = GradientDrawable.LINEAR_GRADIENT; 97dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 98dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mCenterX = 0f; 99dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mCenterY = 0f; 100dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 101dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mStartX = 0f; 102dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mStartY = 0f; 103dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mEndX = 0f; 104dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mEndY = 0f; 105dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 106dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int mStartColor = 0; 107dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int mCenterColor = 0; 108dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int mEndColor = 0; 109dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private boolean mHasCenterColor = false; 110dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 1111664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private int mTileMode = 0; // Clamp mode. 1121664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu 113dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float mGradientRadius = 0f; 114dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 115dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Below are the attributes for the <item> element. 116dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int[] mItemColors; 117dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private float[] mItemOffsets; 118dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 119dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Theme attributes for the root and item elements. 120dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int[] mThemeAttrs; 121dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private int[][] mItemsThemeAttrs; 122dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 123dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private GradientColor() { 124dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 125dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 126dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private GradientColor(GradientColor copy) { 127dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (copy != null) { 128dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mChangingConfigurations = copy.mChangingConfigurations; 129dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mDefaultColor = copy.mDefaultColor; 130dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mShader = copy.mShader; 131dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mGradientType = copy.mGradientType; 132dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterX = copy.mCenterX; 133dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterY = copy.mCenterY; 134dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartX = copy.mStartX; 135dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartY = copy.mStartY; 136dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndX = copy.mEndX; 137dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndY = copy.mEndY; 138dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartColor = copy.mStartColor; 139dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterColor = copy.mCenterColor; 140dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndColor = copy.mEndColor; 141dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mHasCenterColor = copy.mHasCenterColor; 142dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mGradientRadius = copy.mGradientRadius; 1431664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu mTileMode = copy.mTileMode; 144dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 145dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (copy.mItemColors != null) { 146dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemColors = copy.mItemColors.clone(); 147dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 148dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (copy.mItemOffsets != null) { 149dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemOffsets = copy.mItemOffsets.clone(); 150dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 151dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 152dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (copy.mThemeAttrs != null) { 153dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mThemeAttrs = copy.mThemeAttrs.clone(); 154dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 155dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (copy.mItemsThemeAttrs != null) { 156dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemsThemeAttrs = copy.mItemsThemeAttrs.clone(); 157dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 158dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 159dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 160dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 1611664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu // Set the default to clamp mode. 1621664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu private static Shader.TileMode parseTileMode(@GradientTileMode int tileMode) { 1631664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu switch (tileMode) { 1641664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu case TILE_MODE_CLAMP: 1651664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu return Shader.TileMode.CLAMP; 1661664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu case TILE_MODE_REPEAT: 1671664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu return Shader.TileMode.REPEAT; 1681664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu case TILE_MODE_MIRROR: 1691664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu return Shader.TileMode.MIRROR; 1701664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu default: 1711664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu return Shader.TileMode.CLAMP; 1721664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu } 1731664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu } 1741664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu 175dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 176dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Update the root level's attributes, either for inflate or applyTheme. 177dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 178dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void updateRootElementState(TypedArray a) { 179dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Extract the theme attributes, if any. 180dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mThemeAttrs = a.extractThemeAttrs(); 181dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 182dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartX = a.getFloat( 183dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_startX, mStartX); 184dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartY = a.getFloat( 185dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_startY, mStartY); 186dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndX = a.getFloat( 187dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_endX, mEndX); 188dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndY = a.getFloat( 189dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_endY, mEndY); 190dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 191dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterX = a.getFloat( 192dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_centerX, mCenterX); 193dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterY = a.getFloat( 194dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_centerY, mCenterY); 195dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 196dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mGradientType = a.getInt( 197dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_type, mGradientType); 198dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 199dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mStartColor = a.getColor( 200dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_startColor, mStartColor); 201dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mHasCenterColor |= a.hasValue( 202dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_centerColor); 203dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mCenterColor = a.getColor( 204dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_centerColor, mCenterColor); 205dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mEndColor = a.getColor( 206dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColor_endColor, mEndColor); 207dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 2081664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu mTileMode = a.getInt( 2091664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu R.styleable.GradientColor_tileMode, mTileMode); 2101664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu 211dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (DBG_GRADIENT) { 212dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "hasCenterColor is " + mHasCenterColor); 213dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mHasCenterColor) { 214dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "centerColor:" + mCenterColor); 215dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 216dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "startColor: " + mStartColor); 217dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "endColor: " + mEndColor); 2181664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu Log.v(TAG, "tileMode: " + mTileMode); 219dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 220dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 221dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius, 222dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mGradientRadius); 223dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 224dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 225dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 226dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Check if the XML content is valid. 227dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 228dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @throws XmlPullParserException if errors were found. 229dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 230dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void validateXmlContent() throws XmlPullParserException { 231dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mGradientRadius <= 0 232dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu && mGradientType == GradientDrawable.RADIAL_GRADIENT) { 233dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throw new XmlPullParserException( 234dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu "<gradient> tag requires 'gradientRadius' " 235dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu + "attribute with radial type"); 236dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 237dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 238dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 239dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 240dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * The shader information will be applied to the native VectorDrawable's path. 241dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @hide 242dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 243dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public Shader getShader() { 244dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mShader; 245dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 246dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 247dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 248dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * A public method to create GradientColor from a XML resource. 249dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 250dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public static GradientColor createFromXml(Resources r, XmlResourceParser parser, Theme theme) 251dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throws XmlPullParserException, IOException { 252dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final AttributeSet attrs = Xml.asAttributeSet(parser); 253dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 254dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int type; 255dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu while ((type = parser.next()) != XmlPullParser.START_TAG 256dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu && type != XmlPullParser.END_DOCUMENT) { 257dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Seek parser to start tag. 258dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 259dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 260dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (type != XmlPullParser.START_TAG) { 261dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throw new XmlPullParserException("No start tag found"); 262dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 263dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 264dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return createFromXmlInner(r, parser, attrs, theme); 265dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 266dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 267dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 268dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Create from inside an XML document. Called on a parser positioned at a 269dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * tag in an XML document, tries to create a GradientColor from that tag. 270dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 271dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @return A new GradientColor for the current tag. 272dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @throws XmlPullParserException if the current tag is not <gradient> 273dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 274dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @NonNull 275dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu static GradientColor createFromXmlInner(@NonNull Resources r, 276dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme) 277dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throws XmlPullParserException, IOException { 278dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final String name = parser.getName(); 279dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (!name.equals("gradient")) { 280dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throw new XmlPullParserException( 281dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu parser.getPositionDescription() + ": invalid gradient color tag " + name); 282dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 283dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 284dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final GradientColor gradientColor = new GradientColor(); 285dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu gradientColor.inflate(r, parser, attrs, theme); 286dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return gradientColor; 287dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 288dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 289dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 290dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Fill in this object based on the contents of an XML "gradient" element. 291dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 292dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 293dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @NonNull AttributeSet attrs, @Nullable Theme theme) 294dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throws XmlPullParserException, IOException { 295dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final TypedArray a = Resources.obtainAttributes(r, theme, attrs, R.styleable.GradientColor); 296dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu updateRootElementState(a); 297dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mChangingConfigurations |= a.getChangingConfigurations(); 298dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu a.recycle(); 299dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 300dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Check correctness and throw exception if errors found. 301dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu validateXmlContent(); 302dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 303dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu inflateChildElements(r, parser, attrs, theme); 304dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 305dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu onColorsChange(); 306dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 307dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 308dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 309dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Inflates child elements "item"s for each color stop. 310dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 311dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Note that at root level, we need to save ThemeAttrs for theme applied later. 312dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Here similarly, at each child item, we need to save the theme's attributes, and apply theme 313dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * later as applyItemsAttrsTheme(). 314dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 315dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void inflateChildElements(@NonNull Resources r, @NonNull XmlPullParser parser, 316dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @NonNull AttributeSet attrs, @NonNull Theme theme) 317dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throws XmlPullParserException, IOException { 318dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final int innerDepth = parser.getDepth() + 1; 319dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int type; 320dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int depth; 321dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 322dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Pre-allocate the array with some size, for better performance. 323dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu float[] offsetList = new float[20]; 324dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int[] colorList = new int[offsetList.length]; 325dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int[][] themeAttrsList = new int[offsetList.length][]; 326dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 327dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int listSize = 0; 328dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu boolean hasUnresolvedAttrs = false; 329dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 330dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu && ((depth = parser.getDepth()) >= innerDepth 331dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu || type != XmlPullParser.END_TAG)) { 332dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (type != XmlPullParser.START_TAG) { 333dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu continue; 334dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 335dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (depth > innerDepth || !parser.getName().equals("item")) { 336dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu continue; 337dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 338dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 339dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final TypedArray a = Resources.obtainAttributes(r, theme, attrs, 340dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColorItem); 341dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu boolean hasColor = a.hasValue(R.styleable.GradientColorItem_color); 342dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu boolean hasOffset = a.hasValue(R.styleable.GradientColorItem_offset); 343dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (!hasColor || !hasOffset) { 344dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu throw new XmlPullParserException( 345dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu parser.getPositionDescription() 346dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu + ": <item> tag requires a 'color' attribute and a 'offset' " 347dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu + "attribute!"); 348dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 349dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 350dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final int[] themeAttrs = a.extractThemeAttrs(); 351dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int color = a.getColor(R.styleable.GradientColorItem_color, 0); 352dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu float offset = a.getFloat(R.styleable.GradientColorItem_offset, 0); 353dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 354dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (DBG_GRADIENT) { 355dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "new item color " + color + " " + Integer.toHexString(color)); 356dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "offset" + offset); 357dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 358dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mChangingConfigurations |= a.getChangingConfigurations(); 359dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu a.recycle(); 360dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 361dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (themeAttrs != null) { 362dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu hasUnresolvedAttrs = true; 363dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 364dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 365dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu colorList = GrowingArrayUtils.append(colorList, listSize, color); 366dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu offsetList = GrowingArrayUtils.append(offsetList, listSize, offset); 367dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu themeAttrsList = GrowingArrayUtils.append(themeAttrsList, listSize, themeAttrs); 368dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu listSize++; 369dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 370dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (listSize > 0) { 371dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (hasUnresolvedAttrs) { 372dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemsThemeAttrs = new int[listSize][]; 373dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu System.arraycopy(themeAttrsList, 0, mItemsThemeAttrs, 0, listSize); 374dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } else { 375dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemsThemeAttrs = null; 376dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 377dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 378dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemColors = new int[listSize]; 379dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemOffsets = new float[listSize]; 380dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu System.arraycopy(colorList, 0, mItemColors, 0, listSize); 381dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu System.arraycopy(offsetList, 0, mItemOffsets, 0, listSize); 382dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 383dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 384dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 385dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 386dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Apply theme to all the items. 387dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 388dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void applyItemsAttrsTheme(Theme t) { 389dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mItemsThemeAttrs == null) { 390dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return; 391dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 392dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 393dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu boolean hasUnresolvedAttrs = false; 394dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 395dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final int[][] themeAttrsList = mItemsThemeAttrs; 396dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final int N = themeAttrsList.length; 397dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu for (int i = 0; i < N; i++) { 398dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (themeAttrsList[i] != null) { 399dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final TypedArray a = t.resolveAttributes(themeAttrsList[i], 400dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu R.styleable.GradientColorItem); 401dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 402dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Extract the theme attributes, if any, before attempting to 403dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // read from the typed array. This prevents a crash if we have 404dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // unresolved attrs. 405dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]); 406dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (themeAttrsList[i] != null) { 407dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu hasUnresolvedAttrs = true; 408dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 409dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 410dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemColors[i] = a.getColor(R.styleable.GradientColorItem_color, mItemColors[i]); 411dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemOffsets[i] = a.getFloat(R.styleable.GradientColorItem_offset, mItemOffsets[i]); 412dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (DBG_GRADIENT) { 413dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "applyItemsAttrsTheme Colors[i] " + i + " " + 414dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Integer.toHexString(mItemColors[i])); 415dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.v(TAG, "Offsets[i] " + i + " " + mItemOffsets[i]); 416dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 417dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 418dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Account for any configuration changes. 419dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mChangingConfigurations |= a.getChangingConfigurations(); 420dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 421dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu a.recycle(); 422dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 423dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 424dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 425dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (!hasUnresolvedAttrs) { 426dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mItemsThemeAttrs = null; 427dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 428dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 429dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 430dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void onColorsChange() { 431dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int[] tempColors = null; 432dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu float[] tempOffsets = null; 433dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 434dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mItemColors != null) { 435dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu int length = mItemColors.length; 436dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors = new int[length]; 437dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets = new float[length]; 438dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 439dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu for (int i = 0; i < length; i++) { 440dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[i] = mItemColors[i]; 441dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets[i] = mItemOffsets[i]; 442dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 443dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } else { 444dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mHasCenterColor) { 445dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors = new int[3]; 446dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[0] = mStartColor; 447dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[1] = mCenterColor; 448dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[2] = mEndColor; 449dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 450dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets = new float[3]; 451dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets[0] = 0.0f; 452dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Since 0.5f is default value, try to take the one that isn't 0.5f 453dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets[1] = 0.5f; 454dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempOffsets[2] = 1f; 455dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } else { 456dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors = new int[2]; 457dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[0] = mStartColor; 458dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu tempColors[1] = mEndColor; 459dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 460dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 461dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (tempColors.length < 2) { 462dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu Log.w(TAG, "<gradient> tag requires 2 color values specified!" + tempColors.length 463dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu + " " + tempColors); 464dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 465dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 466dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mGradientType == GradientDrawable.LINEAR_GRADIENT) { 467dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets, 4681664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu parseTileMode(mTileMode)); 469dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } else { 470dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mGradientType == GradientDrawable.RADIAL_GRADIENT) { 471dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors, 4721664a829eb08dd5c0f567f6466368ef1d1895e36Teng-Hui Zhu tempOffsets, parseTileMode(mTileMode)); 473dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } else { 474dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets); 475dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 476dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 477dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mDefaultColor = tempColors[0]; 478dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 479dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 480dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 481dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * For Gradient color, the default color is not very useful, since the gradient will override 482dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * the color information anyway. 483dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 484dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 485dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @ColorInt 486dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public int getDefaultColor() { 487dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mDefaultColor; 488dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 489dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 490dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 491dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Similar to ColorStateList, setup constant state and its factory. 492dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @hide only for resource preloading 493dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 494dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 495dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public ConstantState<ComplexColor> getConstantState() { 496dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mFactory == null) { 497dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mFactory = new GradientColorFactory(this); 498dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 499dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mFactory; 500dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 501dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 502dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private static class GradientColorFactory extends ConstantState<ComplexColor> { 503dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private final GradientColor mSrc; 504dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 505dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public GradientColorFactory(GradientColor src) { 506dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mSrc = src; 507dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 508dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 509dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 510ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette public @Config int getChangingConfigurations() { 511dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mSrc.mChangingConfigurations; 512dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 513dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 514dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 515dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public GradientColor newInstance() { 516dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mSrc; 517dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 518dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 519dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 520dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public GradientColor newInstance(Resources res, Theme theme) { 521dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mSrc.obtainForTheme(theme); 522dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 523dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 524dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 525dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 526dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Returns an appropriately themed gradient color. 527dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 528dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @param t the theme to apply 529dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @return a copy of the gradient color the theme applied, or the 530dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * gradient itself if there were no unresolved theme 531dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * attributes 532dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @hide only for resource preloading 533dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 534dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 535dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public GradientColor obtainForTheme(Theme t) { 536dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (t == null || !canApplyTheme()) { 537dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return this; 538dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 539dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 540dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final GradientColor clone = new GradientColor(this); 541dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu clone.applyTheme(t); 542dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return clone; 543dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 544dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 5450b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette /** 5460b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * Returns a mask of the configuration parameters for which this gradient 5470b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * may change, requiring that it be re-created. 5480b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * 5490b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * @return a mask of the changing configuration parameters, as defined by 5500b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * {@link android.content.pm.ActivityInfo} 5510b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * 5520b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette * @see android.content.pm.ActivityInfo 5530b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette */ 5540b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette public int getChangingConfigurations() { 5550b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette return super.getChangingConfigurations() | mChangingConfigurations; 5560b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette } 5570b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette 558dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void applyTheme(Theme t) { 559dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mThemeAttrs != null) { 560dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu applyRootAttrsTheme(t); 561dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 562dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu if (mItemsThemeAttrs != null) { 563dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu applyItemsAttrsTheme(t); 564dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 565dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu onColorsChange(); 566dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 567dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 568dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu private void applyRootAttrsTheme(Theme t) { 569dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.GradientColor); 570dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // mThemeAttrs will be set to null if if there are no theme attributes in the 571dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // typed array. 572dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mThemeAttrs = a.extractThemeAttrs(mThemeAttrs); 573dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // merging the attributes update inside the updateRootElementState(). 574dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu updateRootElementState(a); 575dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 576dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu // Account for any configuration changes. 577dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu mChangingConfigurations |= a.getChangingConfigurations(); 578dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu a.recycle(); 579dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 580dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 581dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 582dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu /** 583dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * Returns whether a theme can be applied to this gradient color, which 584dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * usually indicates that the gradient color has unresolved theme 585dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * attributes. 586dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * 587dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @return whether a theme can be applied to this gradient color. 588dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu * @hide only for resource preloading 589dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu */ 590dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu @Override 591dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu public boolean canApplyTheme() { 592dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu return mThemeAttrs != null || mItemsThemeAttrs != null; 593dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu } 594dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu 595dbee9bb342cdfaa5155b1918f90262c05e2464cbTeng-Hui Zhu} 596