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