com.flipkart.android.proteus.toolbox.ColorUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.flipkart.android.proteus.toolbox.ColorUtils.java

Source

/*
 * Copyright 2016 Flipkart Internet Pvt. Ltd.
 *
 * 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.flipkart.android.proteus.toolbox;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.Log;
import android.util.StateSet;
import android.webkit.ValueCallback;

import com.flipkart.android.proteus.parser.ParseHelper;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class ColorUtils {
    private static final String TAG = "ColorUtils";
    private static HashMap<String, Integer> sAttributesMap = null;

    /**
     * @param context                Application context used to access resources
     * @param value                  JSON representation of the Color
     * @param colorCallback          Callback for return Value if it is a Color Resource
     * @param colorStateListCallback Callback for return Value if it is a ColorStateList
     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
     */
    public static void loadColor(Context context, JsonElement value, ValueCallback<Integer> colorCallback,
            ValueCallback<ColorStateList> colorStateListCallback) throws Resources.NotFoundException {
        if (value.isJsonPrimitive()) {
            handleString(context, value.getAsString(), colorCallback, colorStateListCallback);
        } else if (value.isJsonObject()) {
            handleElement(context, value.getAsJsonObject(), colorCallback, colorStateListCallback);
        } else {
            if (ProteusConstants.isLoggingEnabled()) {
                Log.e(TAG, "Could not color for : " + value.toString());
            }
        }
    }

    private static void handleString(Context context, String attributeValue, ValueCallback<Integer> colorCallback,
            ValueCallback<ColorStateList> colorStateListCallback) {
        if (ParseHelper.isColor(attributeValue)) {
            colorCallback.onReceiveValue(Color.parseColor(attributeValue));
        } else if (ParseHelper.isLocalColorResource(attributeValue)) {
            try {
                Resources r = context.getResources();
                int colorId = r.getIdentifier(attributeValue, "color", context.getPackageName());
                ColorStateList colorStateList = null;
                try {
                    colorStateList = r.getColorStateList(colorId);
                } catch (Resources.NotFoundException nfe) {
                    //assuming this is a color Now
                }
                if (null != colorStateList) {
                    colorStateListCallback.onReceiveValue(colorStateList);
                } else {
                    int color = r.getColor(colorId);
                    colorCallback.onReceiveValue(color);
                }
            } catch (Exception ex) {
                if (ProteusConstants.isLoggingEnabled()) {
                    Log.e(TAG, "Could not load local resource " + attributeValue);
                }
            }
        }
    }

    private static void handleElement(Context context, JsonObject value, ValueCallback<Integer> colorCallback,
            ValueCallback<ColorStateList> colorStateListCallback) {
        JsonObject jsonObject = value.getAsJsonObject();
        ColorStateList colorStateList = inflateFromJson(context, jsonObject);

        if (null != colorStateList) {
            colorStateListCallback.onReceiveValue(colorStateList);
        }
    }

    private static Integer getColorFromAttributeValue(Context context, String attributeValue) {
        Integer result = null;
        if (ParseHelper.isColor(attributeValue)) {
            result = Color.parseColor(attributeValue);
        } else if (ParseHelper.isLocalColorResource(attributeValue)) {
            Resources r = context.getResources();
            int colorId = r.getIdentifier(attributeValue, "color", context.getPackageName());
            result = r.getColor(colorId);
        }
        return result;
    }

    private static int idealByteArraySize(int need) {
        for (int i = 4; i < 32; i++) {
            if (need <= (1 << i) - 12) {
                return (1 << i) - 12;
            }
        }

        return need;
    }

    private static int idealIntArraySize(int need) {
        return idealByteArraySize(need * 4) / 4;
    }

    public static int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }

    private static int modulateColorAlpha(int baseColor, float alphaMod) {
        if (alphaMod == 1.0f) {
            return baseColor;
        }
        final int baseAlpha = Color.alpha(baseColor);
        final int alpha = constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
        return (baseColor & 0xFFFFFF) | (alpha << 24);
    }

    private static HashMap<String, Integer> getAttributesMap() {
        if (null == sAttributesMap) {
            synchronized (ColorUtils.class) {
                if (null == sAttributesMap) {
                    sAttributesMap = new HashMap<>(15);
                    sAttributesMap.put("type", android.R.attr.type);
                    sAttributesMap.put("color", android.R.attr.color);
                    sAttributesMap.put("alpha", android.R.attr.alpha);
                    sAttributesMap.put("state_pressed", android.R.attr.state_pressed);
                    sAttributesMap.put("state_focused", android.R.attr.state_focused);
                    sAttributesMap.put("state_selected", android.R.attr.state_selected);
                    sAttributesMap.put("state_checkable", android.R.attr.state_checkable);
                    sAttributesMap.put("state_checked", android.R.attr.state_checked);
                    sAttributesMap.put("state_enabled", android.R.attr.state_enabled);
                    sAttributesMap.put("state_window_focused", android.R.attr.state_window_focused);
                }
            }
        }
        return sAttributesMap;
    }

    private static Integer getAttribute(String attribute) {
        return getAttributesMap().get(attribute);
    }

    private static ColorStateList inflateFromJson(Context context, JsonObject jsonObject) {
        ColorStateList result = null;
        JsonElement type = jsonObject.get("type");
        if (null != type && type.isJsonPrimitive()) {
            String colorType = type.getAsString();
            if (TextUtils.equals(colorType, "selector")) {
                JsonElement childrenElement = jsonObject.get("children");

                if (null != childrenElement && childrenElement.isJsonArray()) {
                    JsonArray children = childrenElement.getAsJsonArray();
                    int listAllocated = 20;
                    int listSize = 0;
                    int[] colorList = new int[listAllocated];
                    int[][] stateSpecList = new int[listAllocated][];

                    for (int idx = 0; idx < children.size(); idx++) {
                        JsonElement itemObject = children.get(idx);
                        if (!itemObject.isJsonObject()) {
                            continue;
                        }

                        Set<Map.Entry<String, JsonElement>> entrySet = ((JsonObject) itemObject).entrySet();
                        if (entrySet.size() == 0) {
                            continue;
                        }

                        int j = 0;
                        Integer baseColor = null;
                        float alphaMod = 1.0f;

                        int[] stateSpec = new int[entrySet.size() - 1];
                        boolean ignoreItem = false;
                        for (Map.Entry<String, JsonElement> entry : entrySet) {
                            if (ignoreItem) {
                                break;
                            }
                            if (!entry.getValue().isJsonPrimitive()) {
                                continue;
                            }
                            Integer attributeId = getAttribute(entry.getKey());
                            if (null != attributeId) {
                                switch (attributeId) {
                                case android.R.attr.type:
                                    if (!TextUtils.equals("item", entry.getValue().getAsString())) {
                                        ignoreItem = true;
                                    }
                                    break;
                                case android.R.attr.color:
                                    String colorRes = entry.getValue().getAsString();
                                    if (!TextUtils.isEmpty(colorRes)) {
                                        baseColor = getColorFromAttributeValue(context, colorRes);
                                    }
                                    break;
                                case android.R.attr.alpha:
                                    String alphaStr = entry.getValue().getAsString();
                                    if (!TextUtils.isEmpty(alphaStr)) {
                                        alphaMod = Float.parseFloat(alphaStr);
                                    }
                                    break;
                                default:
                                    stateSpec[j++] = entry.getValue().getAsBoolean() ? attributeId : -attributeId;
                                    break;
                                }
                            }
                        }
                        if (!ignoreItem) {
                            stateSpec = StateSet.trimStateSet(stateSpec, j);
                            if (null == baseColor) {
                                throw new IllegalStateException("No Color Specified");
                            }

                            if (listSize + 1 >= listAllocated) {
                                listAllocated = idealIntArraySize(listSize + 1);
                                int[] ncolor = new int[listAllocated];
                                System.arraycopy(colorList, 0, ncolor, 0, listSize);
                                int[][] nstate = new int[listAllocated][];
                                System.arraycopy(stateSpecList, 0, nstate, 0, listSize);
                                colorList = ncolor;
                                stateSpecList = nstate;
                            }

                            final int color = modulateColorAlpha(baseColor, alphaMod);

                            colorList[listSize] = color;
                            stateSpecList[listSize] = stateSpec;
                            listSize++;
                        }
                    }
                    if (listSize > 0) {
                        int[] colors = new int[listSize];
                        int[][] stateSpecs = new int[listSize][];
                        System.arraycopy(colorList, 0, colors, 0, listSize);
                        System.arraycopy(stateSpecList, 0, stateSpecs, 0, listSize);
                        result = new ColorStateList(stateSpecs, colors);
                    }
                }
            }
        }
        return result;
    }
}