1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.contacts.util;
18
19import android.app.Activity;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.Trace;
25import android.support.v4.content.ContextCompat;
26
27import com.android.contacts.R;
28import com.android.contacts.activities.PeopleActivity;
29
30public class MaterialColorMapUtils {
31    private final TypedArray sPrimaryColors;
32    private final TypedArray sSecondaryColors;
33
34    public MaterialColorMapUtils(Resources resources) {
35        sPrimaryColors = resources.obtainTypedArray(
36                com.android.contacts.R.array.letter_tile_colors);
37        sSecondaryColors = resources.obtainTypedArray(
38                com.android.contacts.R.array.letter_tile_colors_dark);
39    }
40
41    public static class MaterialPalette implements Parcelable {
42        public MaterialPalette(int primaryColor, int secondaryColor) {
43            mPrimaryColor = primaryColor;
44            mSecondaryColor = secondaryColor;
45        }
46        public final int mPrimaryColor;
47        public final int mSecondaryColor;
48
49        @Override
50        public boolean equals(Object obj) {
51            if (this == obj) {
52                return true;
53            }
54            if (obj == null) {
55                return false;
56            }
57            if (getClass() != obj.getClass()) {
58                return false;
59            }
60            MaterialPalette other = (MaterialPalette) obj;
61            if (mPrimaryColor != other.mPrimaryColor) {
62                return false;
63            }
64            if (mSecondaryColor != other.mSecondaryColor) {
65                return false;
66            }
67            return true;
68        }
69
70        @Override
71        public int hashCode() {
72            final int prime = 31;
73            int result = 1;
74            result = prime * result + mPrimaryColor;
75            result = prime * result + mSecondaryColor;
76            return result;
77        }
78
79        @Override
80        public int describeContents() {
81            return 0;
82        }
83
84        @Override
85        public void writeToParcel(Parcel dest, int flags) {
86            dest.writeInt(mPrimaryColor);
87            dest.writeInt(mSecondaryColor);
88        }
89
90        private MaterialPalette(Parcel in) {
91            mPrimaryColor = in.readInt();
92            mSecondaryColor = in.readInt();
93        }
94
95        public static final Creator<MaterialPalette> CREATOR = new Creator<MaterialPalette>() {
96                @Override
97                public MaterialPalette createFromParcel(Parcel in) {
98                    return new MaterialPalette(in);
99                }
100
101                @Override
102                public MaterialPalette[] newArray(int size) {
103                    return new MaterialPalette[size];
104                }
105        };
106    }
107
108    /**
109     * Return primary and secondary colors from the Material color palette that are similar to
110     * {@param color}.
111     */
112    public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
113        Trace.beginSection("calculatePrimaryAndSecondaryColor");
114
115        final float colorHue = hue(color);
116        float minimumDistance = Float.MAX_VALUE;
117        int indexBestMatch = 0;
118        for (int i = 0; i < sPrimaryColors.length(); i++) {
119            final int primaryColor = sPrimaryColors.getColor(i, 0);
120            final float comparedHue = hue(primaryColor);
121            // No need to be perceptually accurate when calculating color distances since
122            // we are only mapping to 15 colors. Being slightly inaccurate isn't going to change
123            // the mapping very often.
124            final float distance = Math.abs(comparedHue - colorHue);
125            if (distance < minimumDistance) {
126                minimumDistance = distance;
127                indexBestMatch = i;
128            }
129        }
130
131        Trace.endSection();
132        return new MaterialPalette(sPrimaryColors.getColor(indexBestMatch, 0),
133                sSecondaryColors.getColor(indexBestMatch, 0));
134    }
135
136    public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) {
137        final int primaryColor = resources.getColor(
138                R.color.quickcontact_default_photo_tint_color);
139        final int secondaryColor = resources.getColor(
140                R.color.quickcontact_default_photo_tint_color_dark);
141        return new MaterialPalette(primaryColor, secondaryColor);
142    }
143
144    /**
145     * Returns the hue component of a color int.
146     *
147     * @return A value between 0.0f and 1.0f
148     */
149    public static float hue(int color) {
150        int r = (color >> 16) & 0xFF;
151        int g = (color >> 8) & 0xFF;
152        int b = color & 0xFF;
153
154        int V = Math.max(b, Math.max(r, g));
155        int temp = Math.min(b, Math.min(r, g));
156
157        float H;
158
159        if (V == temp) {
160            H = 0;
161        } else {
162            final float vtemp = V - temp;
163            final float cr = (V - r) / vtemp;
164            final float cg = (V - g) / vtemp;
165            final float cb = (V - b) / vtemp;
166
167            if (r == V) {
168                H = cb - cg;
169            } else if (g == V) {
170                H = 2 + cr - cb;
171            } else {
172                H = 4 + cg - cr;
173            }
174
175            H /= 6.f;
176            if (H < 0) {
177                H++;
178            }
179        }
180
181        return H;
182    }
183
184    /**
185     * Returns status bar color for group view and non-group views.
186     */
187    public static int getStatusBarColor(Activity activity) {
188        final boolean isGroupView = activity instanceof PeopleActivity
189                && ((PeopleActivity) activity).isGroupView();
190        return isGroupView
191                ? ContextCompat.getColor(activity, R.color.group_primary_color_dark)
192                : ContextCompat.getColor(activity, R.color.primary_color_dark);
193    }
194
195    /**
196     * Returns toolbar color for group view and non-group views.
197     */
198    public static int getToolBarColor(Activity activity) {
199        final boolean isGroupView = activity instanceof PeopleActivity
200                && ((PeopleActivity) activity).isGroupView();
201        return isGroupView
202                ? ContextCompat.getColor(activity, R.color.group_primary_color)
203                : ContextCompat.getColor(activity, R.color.primary_color);
204    }
205}
206