1528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell/*
2528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * Copyright (C) 2014 The Android Open Source Project
3528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell *
4528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * Licensed under the Apache License, Version 2.0 (the "License");
5528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * you may not use this file except in compliance with the License.
6528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * You may obtain a copy of the License at
7528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell *
8528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell *      http://www.apache.org/licenses/LICENSE-2.0
9528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell *
10528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * Unless required by applicable law or agreed to in writing, software
11528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * distributed under the License is distributed on an "AS IS" BASIS,
12528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * See the License for the specific language governing permissions and
14528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell * limitations under the License.
15528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell */
16528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
17528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwellpackage com.android.contacts.common.util;
18528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
19d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwellimport com.android.contacts.common.R;
20d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell
21d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwellimport android.content.res.Resources;
22d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwellimport android.content.res.TypedArray;
239824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Leeimport android.os.Parcel;
249824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Leeimport android.os.Parcelable;
25528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwellimport android.os.Trace;
26528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
27528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwellpublic class MaterialColorMapUtils {
28d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    private final TypedArray sPrimaryColors;
29d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    private final TypedArray sSecondaryColors;
30528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
31d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    public MaterialColorMapUtils(Resources resources) {
32d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        sPrimaryColors = resources.obtainTypedArray(
33d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell                com.android.contacts.common.R.array.letter_tile_colors);
34d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        sSecondaryColors = resources.obtainTypedArray(
35d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell                com.android.contacts.common.R.array.letter_tile_colors_dark);
36d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    }
37528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
389824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee    public static class MaterialPalette implements Parcelable {
39528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        public MaterialPalette(int primaryColor, int secondaryColor) {
40528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            mPrimaryColor = primaryColor;
41528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            mSecondaryColor = secondaryColor;
42528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        }
43528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        public final int mPrimaryColor;
44528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        public final int mSecondaryColor;
45dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen
46dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        @Override
47dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        public boolean equals(Object obj) {
48dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            if (this == obj) {
49dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen                return true;
50dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            }
51dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            if (obj == null) {
52dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen                return false;
53dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            }
54dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            if (getClass() != obj.getClass()) {
55dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen                return false;
56dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            }
57dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            MaterialPalette other = (MaterialPalette) obj;
58dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            if (mPrimaryColor != other.mPrimaryColor) {
59dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen                return false;
60dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            }
61dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            if (mSecondaryColor != other.mSecondaryColor) {
62dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen                return false;
63dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            }
64dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            return true;
65dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        }
66dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen
67dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        @Override
68dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        public int hashCode() {
69dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            final int prime = 31;
70dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            int result = 1;
71dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            result = prime * result + mPrimaryColor;
72dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            result = prime * result + mSecondaryColor;
73dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen            return result;
74dec173e8f92b8bf3e7864f9931ae0ab75eff9bc6Nancy Chen        }
759824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee
769824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        @Override
779824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        public int describeContents() {
789824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee            return 0;
799824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        }
809824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee
819824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        @Override
829824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        public void writeToParcel(Parcel dest, int flags) {
839824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee            dest.writeInt(mPrimaryColor);
849824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee            dest.writeInt(mSecondaryColor);
859824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        }
869824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee
879824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        private MaterialPalette(Parcel in) {
889824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee            mPrimaryColor = in.readInt();
899824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee            mSecondaryColor = in.readInt();
909824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        }
919824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee
929824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        public static final Creator<MaterialPalette> CREATOR = new Creator<MaterialPalette>() {
939824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                @Override
949824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                public MaterialPalette createFromParcel(Parcel in) {
959824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                    return new MaterialPalette(in);
969824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                }
979824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee
989824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                @Override
999824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                public MaterialPalette[] newArray(int size) {
1009824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                    return new MaterialPalette[size];
1019824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee                }
1029824b1a62110f2ee9caefa8ede7d213c176ca012Yorke Lee        };
103528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell    }
104528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
105528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell    /**
106d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell     * Return primary and secondary colors from the Material color palette that are similar to
107d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell     * {@param color}.
108528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell     */
109d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
110528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        Trace.beginSection("calculatePrimaryAndSecondaryColor");
111528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
112d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        final float colorHue = hue(color);
113528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        float minimumDistance = Float.MAX_VALUE;
114528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        int indexBestMatch = 0;
115d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        for (int i = 0; i < sPrimaryColors.length(); i++) {
116d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell            final int primaryColor = sPrimaryColors.getColor(i, 0);
117d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell            final float comparedHue = hue(primaryColor);
118528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            // No need to be perceptually accurate when calculating color distances since
119528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            // we are only mapping to 15 colors. Being slightly inaccurate isn't going to change
120528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            // the mapping very often.
121528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            final float distance = Math.abs(comparedHue - colorHue);
122528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            if (distance < minimumDistance) {
123528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell                minimumDistance = distance;
124528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell                indexBestMatch = i;
125528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell            }
126528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        }
127528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
128528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell        Trace.endSection();
129d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        return new MaterialPalette(sPrimaryColors.getColor(indexBestMatch, 0),
130d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell                sSecondaryColors.getColor(indexBestMatch, 0));
131528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell    }
132528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
133d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell    public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) {
134d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        final int primaryColor = resources.getColor(
135d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell                R.color.quickcontact_default_photo_tint_color);
136d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        final int secondaryColor = resources.getColor(
137d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell                R.color.quickcontact_default_photo_tint_color_dark);
138d4d290d39d14e17683ec86b07a150c9df35505aeBrian Attwell        return new MaterialPalette(primaryColor, secondaryColor);
139528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell    }
140528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell
141d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee    /**
142d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee     * Returns the hue component of a color int.
143d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee     *
144d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee     * @return A value between 0.0f and 1.0f
145d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee     */
146d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee    public static float hue(int color) {
147d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        int r = (color >> 16) & 0xFF;
148d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        int g = (color >> 8) & 0xFF;
149d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        int b = color & 0xFF;
150d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
151d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        int V = Math.max(b, Math.max(r, g));
152d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        int temp = Math.min(b, Math.min(r, g));
153d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
154d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        float H;
155d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
156d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        if (V == temp) {
157d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            H = 0;
158d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        } else {
159d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            final float vtemp = V - temp;
160d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            final float cr = (V - r) / vtemp;
161d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            final float cg = (V - g) / vtemp;
162d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            final float cb = (V - b) / vtemp;
163d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
164d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            if (r == V) {
165d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee                H = cb - cg;
166d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            } else if (g == V) {
167d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee                H = 2 + cr - cb;
168d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            } else {
169d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee                H = 4 + cg - cr;
170d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            }
171d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
172d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            H /= 6.f;
173d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            if (H < 0) {
174d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee                H++;
175d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee            }
176d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        }
177d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee
178d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee        return H;
179d585ac1c9ea385751b69897a9efb907655296f66Yorke Lee    }
180528cb5eae10cc221cdc989035b0dc5e21f09684bBrian Attwell}
181