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 }; 74 75 public static int HAPPY = 0; 76 public static int SAD = 1; 77 public static int WINKING = 2; 78 public static int TONGUE_STICKING_OUT = 3; 79 public static int SURPRISED = 4; 80 public static int KISSING = 5; 81 public static int YELLING = 6; 82 public static int COOL = 7; 83 public static int MONEY_MOUTH = 8; 84 public static int FOOT_IN_MOUTH = 9; 85 public static int EMBARRASSED = 10; 86 public static int ANGEL = 11; 87 public static int UNDECIDED = 12; 88 public static int CRYING = 13; 89 public static int LIPS_ARE_SEALED = 14; 90 public static int LAUGHING = 15; 91 public static int WTF = 16; 92 93 public static int getSmileyResource(int which) { 94 return sIconIds[which]; 95 } 96 } 97 98 // NOTE: if you change anything about this array, you must make the corresponding change 99 // to the string arrays: default_smiley_texts and default_smiley_names in res/values/arrays.xml 100 public static final int[] DEFAULT_SMILEY_RES_IDS = { 101 Smileys.getSmileyResource(Smileys.HAPPY), // 0 102 Smileys.getSmileyResource(Smileys.SAD), // 1 103 Smileys.getSmileyResource(Smileys.WINKING), // 2 104 Smileys.getSmileyResource(Smileys.TONGUE_STICKING_OUT), // 3 105 Smileys.getSmileyResource(Smileys.SURPRISED), // 4 106 Smileys.getSmileyResource(Smileys.KISSING), // 5 107 Smileys.getSmileyResource(Smileys.YELLING), // 6 108 Smileys.getSmileyResource(Smileys.COOL), // 7 109 Smileys.getSmileyResource(Smileys.MONEY_MOUTH), // 8 110 Smileys.getSmileyResource(Smileys.FOOT_IN_MOUTH), // 9 111 Smileys.getSmileyResource(Smileys.EMBARRASSED), // 10 112 Smileys.getSmileyResource(Smileys.ANGEL), // 11 113 Smileys.getSmileyResource(Smileys.UNDECIDED), // 12 114 Smileys.getSmileyResource(Smileys.CRYING), // 13 115 Smileys.getSmileyResource(Smileys.LIPS_ARE_SEALED), // 14 116 Smileys.getSmileyResource(Smileys.LAUGHING), // 15 117 Smileys.getSmileyResource(Smileys.WTF), // 16 118 }; 119 120 public static final int DEFAULT_SMILEY_TEXTS = R.array.default_smiley_texts; 121 public static final int DEFAULT_SMILEY_NAMES = R.array.default_smiley_names; 122 123 /** 124 * Builds the hashtable we use for mapping the string version 125 * of a smiley (e.g. ":-)") to a resource ID for the icon version. 126 */ 127 private HashMap<String, Integer> buildSmileyToRes() { 128 if (DEFAULT_SMILEY_RES_IDS.length != mSmileyTexts.length) { 129 // Throw an exception if someone updated DEFAULT_SMILEY_RES_IDS 130 // and failed to update arrays.xml 131 throw new IllegalStateException("Smiley resource ID/text mismatch"); 132 } 133 134 HashMap<String, Integer> smileyToRes = 135 new HashMap<String, Integer>(mSmileyTexts.length); 136 for (int i = 0; i < mSmileyTexts.length; i++) { 137 smileyToRes.put(mSmileyTexts[i], DEFAULT_SMILEY_RES_IDS[i]); 138 } 139 140 return smileyToRes; 141 } 142 143 /** 144 * Builds the regular expression we use to find smileys in {@link #addSmileySpans}. 145 */ 146 private Pattern buildPattern() { 147 // Set the StringBuilder capacity with the assumption that the average 148 // smiley is 3 characters long. 149 StringBuilder patternString = new StringBuilder(mSmileyTexts.length * 3); 150 151 // Build a regex that looks like (:-)|:-(|...), but escaping the smilies 152 // properly so they will be interpreted literally by the regex matcher. 153 patternString.append('('); 154 for (String s : mSmileyTexts) { 155 patternString.append(Pattern.quote(s)); 156 patternString.append('|'); 157 } 158 // Replace the extra '|' with a ')' 159 patternString.replace(patternString.length() - 1, patternString.length(), ")"); 160 161 return Pattern.compile(patternString.toString()); 162 } 163 164 165 /** 166 * Adds ImageSpans to a CharSequence that replace textual emoticons such 167 * as :-) with a graphical version. 168 * 169 * @param text A CharSequence possibly containing emoticons 170 * @return A CharSequence annotated with ImageSpans covering any 171 * recognized emoticons. 172 */ 173 public CharSequence addSmileySpans(CharSequence text) { 174 SpannableStringBuilder builder = new SpannableStringBuilder(text); 175 176 Matcher matcher = mPattern.matcher(text); 177 while (matcher.find()) { 178 int resId = mSmileyToRes.get(matcher.group()); 179 builder.setSpan(new ImageSpan(mContext, resId), 180 matcher.start(), matcher.end(), 181 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 182 } 183 184 return builder; 185 } 186 } 187 188 189