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 17 package com.android.contacts.common.util; 18 19 import com.android.contacts.common.R; 20 21 import android.content.res.Resources; 22 import android.content.res.TypedArray; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.os.Trace; 26 27 public class MaterialColorMapUtils { 28 private final TypedArray sPrimaryColors; 29 private final TypedArray sSecondaryColors; 30 31 public MaterialColorMapUtils(Resources resources) { 32 sPrimaryColors = resources.obtainTypedArray( 33 com.android.contacts.common.R.array.letter_tile_colors); 34 sSecondaryColors = resources.obtainTypedArray( 35 com.android.contacts.common.R.array.letter_tile_colors_dark); 36 } 37 38 public static class MaterialPalette implements Parcelable { 39 public MaterialPalette(int primaryColor, int secondaryColor) { 40 mPrimaryColor = primaryColor; 41 mSecondaryColor = secondaryColor; 42 } 43 public final int mPrimaryColor; 44 public final int mSecondaryColor; 45 46 @Override 47 public boolean equals(Object obj) { 48 if (this == obj) { 49 return true; 50 } 51 if (obj == null) { 52 return false; 53 } 54 if (getClass() != obj.getClass()) { 55 return false; 56 } 57 MaterialPalette other = (MaterialPalette) obj; 58 if (mPrimaryColor != other.mPrimaryColor) { 59 return false; 60 } 61 if (mSecondaryColor != other.mSecondaryColor) { 62 return false; 63 } 64 return true; 65 } 66 67 @Override 68 public int hashCode() { 69 final int prime = 31; 70 int result = 1; 71 result = prime * result + mPrimaryColor; 72 result = prime * result + mSecondaryColor; 73 return result; 74 } 75 76 @Override 77 public int describeContents() { 78 return 0; 79 } 80 81 @Override 82 public void writeToParcel(Parcel dest, int flags) { 83 dest.writeInt(mPrimaryColor); 84 dest.writeInt(mSecondaryColor); 85 } 86 87 private MaterialPalette(Parcel in) { 88 mPrimaryColor = in.readInt(); 89 mSecondaryColor = in.readInt(); 90 } 91 92 public static final Creator<MaterialPalette> CREATOR = new Creator<MaterialPalette>() { 93 @Override 94 public MaterialPalette createFromParcel(Parcel in) { 95 return new MaterialPalette(in); 96 } 97 98 @Override 99 public MaterialPalette[] newArray(int size) { 100 return new MaterialPalette[size]; 101 } 102 }; 103 } 104 105 /** 106 * Return primary and secondary colors from the Material color palette that are similar to 107 * {@param color}. 108 */ 109 public MaterialPalette calculatePrimaryAndSecondaryColor(int color) { 110 Trace.beginSection("calculatePrimaryAndSecondaryColor"); 111 112 final float colorHue = hue(color); 113 float minimumDistance = Float.MAX_VALUE; 114 int indexBestMatch = 0; 115 for (int i = 0; i < sPrimaryColors.length(); i++) { 116 final int primaryColor = sPrimaryColors.getColor(i, 0); 117 final float comparedHue = hue(primaryColor); 118 // No need to be perceptually accurate when calculating color distances since 119 // we are only mapping to 15 colors. Being slightly inaccurate isn't going to change 120 // the mapping very often. 121 final float distance = Math.abs(comparedHue - colorHue); 122 if (distance < minimumDistance) { 123 minimumDistance = distance; 124 indexBestMatch = i; 125 } 126 } 127 128 Trace.endSection(); 129 return new MaterialPalette(sPrimaryColors.getColor(indexBestMatch, 0), 130 sSecondaryColors.getColor(indexBestMatch, 0)); 131 } 132 133 public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) { 134 final int primaryColor = resources.getColor( 135 R.color.quickcontact_default_photo_tint_color); 136 final int secondaryColor = resources.getColor( 137 R.color.quickcontact_default_photo_tint_color_dark); 138 return new MaterialPalette(primaryColor, secondaryColor); 139 } 140 141 /** 142 * Returns the hue component of a color int. 143 * 144 * @return A value between 0.0f and 1.0f 145 */ 146 public static float hue(int color) { 147 int r = (color >> 16) & 0xFF; 148 int g = (color >> 8) & 0xFF; 149 int b = color & 0xFF; 150 151 int V = Math.max(b, Math.max(r, g)); 152 int temp = Math.min(b, Math.min(r, g)); 153 154 float H; 155 156 if (V == temp) { 157 H = 0; 158 } else { 159 final float vtemp = V - temp; 160 final float cr = (V - r) / vtemp; 161 final float cg = (V - g) / vtemp; 162 final float cb = (V - b) / vtemp; 163 164 if (r == V) { 165 H = cb - cg; 166 } else if (g == V) { 167 H = 2 + cr - cb; 168 } else { 169 H = 4 + cg - cr; 170 } 171 172 H /= 6.f; 173 if (H < 0) { 174 H++; 175 } 176 } 177 178 return H; 179 } 180 } 181