1 /* 2 * Copyright (C) 2009 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.mms.util; 18 19 import android.content.Context; 20 import android.text.Spannable; 21 import android.text.SpannableStringBuilder; 22 import android.text.style.ImageSpan; 23 24 import com.android.mms.R; 25 26 import java.util.HashMap; 27 import java.util.regex.Matcher; 28 import java.util.regex.Pattern; 29 30 /** 31 * A class for annotating a CharSequence with spans to convert textual emoticons 32 * to graphical ones. 33 */ 34 public class SmileyParser { 35 // Singleton stuff 36 private static SmileyParser sInstance; 37 public static SmileyParser getInstance() { return sInstance; } 38 public static void init(Context context) { 39 sInstance = new SmileyParser(context); 40 } 41 42 private final Context mContext; 43 private final String[] mSmileyTexts; 44 private final Pattern mPattern; 45 private final HashMap<String, Integer> mSmileyToRes; 46 47 private SmileyParser(Context context) { 48 mContext = context; 49 mSmileyTexts = mContext.getResources().getStringArray(DEFAULT_SMILEY_TEXTS); 50 mSmileyToRes = buildSmileyToRes(); 51 mPattern = buildPattern(); 52 } 53 54 static class Smileys { 55 private static final int[] sIconIds = { 56 R.drawable.emo_im_happy, 57 R.drawable.emo_im_sad, 58 R.drawable.emo_im_winking, 59 R.drawable.emo_im_tongue_sticking_out, 60 R.drawable.emo_im_surprised, 61 R.drawable.emo_im_kissing, 62 R.drawable.emo_im_yelling, 63 R.drawable.emo_im_cool, 64 R.drawable.emo_im_money_mouth, 65 R.drawable.emo_im_foot_in_mouth, 66 R.drawable.emo_im_embarrassed, 67 R.drawable.emo_im_angel, 68 R.drawable.emo_im_undecided, 69 R.drawable.emo_im_crying, 70 R.drawable.emo_im_lips_are_sealed, 71 R.drawable.emo_im_laughing, 72 R.drawable.emo_im_wtf, 73 R.drawable.emo_im_heart, 74 R.drawable.emo_im_mad, 75 R.drawable.emo_im_smirk, 76 R.drawable.emo_im_pokerface 77 }; 78 79 public static int HAPPY = 0; 80 public static int SAD = 1; 81 public static int WINKING = 2; 82 public static int TONGUE_STICKING_OUT = 3; 83 public static int SURPRISED = 4; 84 public static int KISSING = 5; 85 public static int YELLING = 6; 86 public static int COOL = 7; 87 public static int MONEY_MOUTH = 8; 88 public static int FOOT_IN_MOUTH = 9; 89 public static int EMBARRASSED = 10; 90 public static int ANGEL = 11; 91 public static int UNDECIDED = 12; 92 public static int CRYING = 13; 93 public static int LIPS_ARE_SEALED = 14; 94 public static int LAUGHING = 15; 95 public static int WTF = 16; 96 public static int MAD = 17; 97 public static int HEART = 18; 98 public static int SMIRK = 19; 99 public static int POKERFACE = 20; 100 101 public static int getSmileyResource(int which) { 102 return sIconIds[which]; 103 } 104 } 105 106 // NOTE: if you change anything about this array, you must make the corresponding change 107 // to the string arrays: default_smiley_texts and default_smiley_names in res/values/arrays.xml 108 public static final int[] DEFAULT_SMILEY_RES_IDS = { 109 Smileys.getSmileyResource(Smileys.HAPPY), // 0 110 Smileys.getSmileyResource(Smileys.SAD), // 1 111 Smileys.getSmileyResource(Smileys.WINKING), // 2 112 Smileys.getSmileyResource(Smileys.TONGUE_STICKING_OUT), // 3 113 Smileys.getSmileyResource(Smileys.SURPRISED), // 4 114 Smileys.getSmileyResource(Smileys.KISSING), // 5 115 Smileys.getSmileyResource(Smileys.YELLING), // 6 116 Smileys.getSmileyResource(Smileys.COOL), // 7 117 Smileys.getSmileyResource(Smileys.MONEY_MOUTH), // 8 118 Smileys.getSmileyResource(Smileys.FOOT_IN_MOUTH), // 9 119 Smileys.getSmileyResource(Smileys.EMBARRASSED), // 10 120 Smileys.getSmileyResource(Smileys.ANGEL), // 11 121 Smileys.getSmileyResource(Smileys.UNDECIDED), // 12 122 Smileys.getSmileyResource(Smileys.CRYING), // 13 123 Smileys.getSmileyResource(Smileys.LIPS_ARE_SEALED), // 14 124 Smileys.getSmileyResource(Smileys.LAUGHING), // 15 125 Smileys.getSmileyResource(Smileys.WTF), // 16 126 Smileys.getSmileyResource(Smileys.MAD), // 17 127 Smileys.getSmileyResource(Smileys.HEART), // 18 128 Smileys.getSmileyResource(Smileys.SMIRK), // 19 129 Smileys.getSmileyResource(Smileys.POKERFACE), // 20 130 }; 131 132 public static final int DEFAULT_SMILEY_TEXTS = R.array.default_smiley_texts; 133 public static final int DEFAULT_SMILEY_NAMES = R.array.default_smiley_names; 134 135 /** 136 * Builds the hashtable we use for mapping the string version 137 * of a smiley (e.g. ":-)") to a resource ID for the icon version. 138 */ 139 private HashMap<String, Integer> buildSmileyToRes() { 140 if (DEFAULT_SMILEY_RES_IDS.length != mSmileyTexts.length) { 141 // Throw an exception if someone updated DEFAULT_SMILEY_RES_IDS 142 // and failed to update arrays.xml 143 throw new IllegalStateException("Smiley resource ID/text mismatch"); 144 } 145 146 HashMap<String, Integer> smileyToRes = 147 new HashMap<String, Integer>(mSmileyTexts.length); 148 for (int i = 0; i < mSmileyTexts.length; i++) { 149 smileyToRes.put(mSmileyTexts[i], DEFAULT_SMILEY_RES_IDS[i]); 150 } 151 152 return smileyToRes; 153 } 154 155 /** 156 * Builds the regular expression we use to find smileys in {@link #addSmileySpans}. 157 */ 158 private Pattern buildPattern() { 159 // Set the StringBuilder capacity with the assumption that the average 160 // smiley is 3 characters long. 161 StringBuilder patternString = new StringBuilder(mSmileyTexts.length * 3); 162 163 // Build a regex that looks like (:-)|:-(|...), but escaping the smilies 164 // properly so they will be interpreted literally by the regex matcher. 165 patternString.append('('); 166 for (String s : mSmileyTexts) { 167 patternString.append(Pattern.quote(s)); 168 patternString.append('|'); 169 } 170 // Replace the extra '|' with a ')' 171 patternString.replace(patternString.length() - 1, patternString.length(), ")"); 172 173 return Pattern.compile(patternString.toString()); 174 } 175 176 177 /** 178 * Adds ImageSpans to a CharSequence that replace textual emoticons such 179 * as :-) with a graphical version. 180 * 181 * @param text A CharSequence possibly containing emoticons 182 * @return A CharSequence annotated with ImageSpans covering any 183 * recognized emoticons. 184 */ 185 public CharSequence addSmileySpans(CharSequence text) { 186 SpannableStringBuilder builder = new SpannableStringBuilder(text); 187 188 Matcher matcher = mPattern.matcher(text); 189 while (matcher.find()) { 190 int resId = mSmileyToRes.get(matcher.group()); 191 builder.setSpan(new ImageSpan(mContext, resId), 192 matcher.start(), matcher.end(), 193 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 194 } 195 196 return builder; 197 } 198 } 199 200 201