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