/* * Copyright (C) 2016 Bilibili * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.bilibili.magicasakura.utils; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Color; import android.support.annotation.RestrictTo; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.LinkedList; import static android.support.annotation.RestrictTo.Scope.LIBRARY; /** * @author [email protected] * @time 16/2/22 */ @RestrictTo(LIBRARY) final class ColorStateListUtils { static ColorStateList createColorStateList(Context context, int resId) { if (resId <= 0) return null; TypedValue value = new TypedValue(); context.getResources().getValue(resId, value, true); ColorStateList cl = null; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { //Assume that "color/theme_color_primary" and "color/theme_color_profile" have the same color value; //However, "color/theme_color_primary" need to replace by themeId, "color/theme_color_profile" not. //If use value.data may cause "color/theme_color_profile" still been replaced by themeId cl = ColorStateList.valueOf(ThemeUtils.replaceColorById(context, value.resourceId)); } else { final String file = value.string.toString(); try { if (file.endsWith("xml")) { final XmlResourceParser rp = context.getResources().getAssets().openXmlResourceParser( value.assetCookie, file); final AttributeSet attrs = Xml.asAttributeSet(rp); int type; while ((type = rp.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Seek parser to start tag. } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException("No start tag found"); } cl = createFromXmlInner(context, rp, attrs); rp.close(); } } catch (IOException e) { e.printStackTrace(); } catch (XmlPullParserException e) { e.printStackTrace(); } } return cl; } static ColorStateList createFromXmlInner(Context context, XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException { final String name = parser.getName(); if (!name.equals("selector")) { throw new XmlPullParserException( parser.getPositionDescription() + ": invalid color state list tag " + name); } return inflateColorStateList(context, parser, attrs); } static ColorStateList inflateColorStateList(Context context, XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException { final int innerDepth = parser.getDepth() + 1; int depth; int type; LinkedList<int[]> stateList = new LinkedList<>(); LinkedList<Integer> colorList = new LinkedList<>(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { if (type != XmlPullParser.START_TAG || depth > innerDepth || !parser.getName().equals("item")) { continue; } TypedArray a1 = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.color}); final int value = a1.getResourceId(0, Color.MAGENTA); final int baseColor = value == Color.MAGENTA ? Color.MAGENTA : ThemeUtils.replaceColorById(context, value); a1.recycle(); TypedArray a2 = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.alpha}); final float alphaMod = a2.getFloat(0, 1.0f); a2.recycle(); colorList.add(alphaMod != 1.0f ? ColorUtils.setAlphaComponent(baseColor, Math.round(Color.alpha(baseColor) * alphaMod)) : baseColor); stateList.add(extractStateSet(attrs)); } if (stateList.size() > 0 && stateList.size() == colorList.size()) { int[] colors = new int[colorList.size()]; for (int i = 0; i < colorList.size(); i++) { colors[i] = colorList.get(i); } return new ColorStateList(stateList.toArray(new int[stateList.size()][]), colors); } return null; } protected static int[] extractStateSet(AttributeSet attrs) { int j = 0; final int numAttrs = attrs.getAttributeCount(); int[] states = new int[numAttrs]; for (int i = 0; i < numAttrs; i++) { final int stateResId = attrs.getAttributeNameResource(i); switch (stateResId) { case 0: break; case android.R.attr.color: case android.R.attr.alpha: // Ignore attributes from StateListDrawableItem and // AnimatedStateListDrawableItem. continue; default: states[j++] = attrs.getAttributeBooleanValue(i, false) ? stateResId : -stateResId; } } states = StateSet.trimStateSet(states, j); return states; } }