/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mms.util; import android.content.Context; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import com.android.mms.R; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A class for annotating a CharSequence with spans to convert textual emoticons * to graphical ones. */ public class SmileyParser { // Singleton stuff private static SmileyParser sInstance; public static SmileyParser getInstance() { return sInstance; } public static void init(Context context) { sInstance = new SmileyParser(context); } private final Context mContext; private final String[] mSmileyTexts; private final Pattern mPattern; private final HashMap<String, Integer> mSmileyToRes; private SmileyParser(Context context) { mContext = context; mSmileyTexts = mContext.getResources().getStringArray(DEFAULT_SMILEY_TEXTS); mSmileyToRes = buildSmileyToRes(); mPattern = buildPattern(); } static class Smileys { private static final int[] sIconIds = { R.drawable.emo_im_happy, R.drawable.emo_im_sad, R.drawable.emo_im_winking, R.drawable.emo_im_tongue_sticking_out, R.drawable.emo_im_surprised, R.drawable.emo_im_kissing, R.drawable.emo_im_yelling, R.drawable.emo_im_cool, R.drawable.emo_im_money_mouth, R.drawable.emo_im_foot_in_mouth, R.drawable.emo_im_embarrassed, R.drawable.emo_im_angel, R.drawable.emo_im_undecided, R.drawable.emo_im_crying, R.drawable.emo_im_lips_are_sealed, R.drawable.emo_im_laughing, R.drawable.emo_im_wtf, R.drawable.emo_im_heart, R.drawable.emo_im_mad, R.drawable.emo_im_smirk, R.drawable.emo_im_pokerface }; public static int HAPPY = 0; public static int SAD = 1; public static int WINKING = 2; public static int TONGUE_STICKING_OUT = 3; public static int SURPRISED = 4; public static int KISSING = 5; public static int YELLING = 6; public static int COOL = 7; public static int MONEY_MOUTH = 8; public static int FOOT_IN_MOUTH = 9; public static int EMBARRASSED = 10; public static int ANGEL = 11; public static int UNDECIDED = 12; public static int CRYING = 13; public static int LIPS_ARE_SEALED = 14; public static int LAUGHING = 15; public static int WTF = 16; public static int MAD = 17; public static int HEART = 18; public static int SMIRK = 19; public static int POKERFACE = 20; public static int getSmileyResource(int which) { return sIconIds[which]; } } // NOTE: if you change anything about this array, you must make the corresponding change // to the string arrays: default_smiley_texts and default_smiley_names in res/values/arrays.xml public static final int[] DEFAULT_SMILEY_RES_IDS = { Smileys.getSmileyResource(Smileys.HAPPY), // 0 Smileys.getSmileyResource(Smileys.SAD), // 1 Smileys.getSmileyResource(Smileys.WINKING), // 2 Smileys.getSmileyResource(Smileys.TONGUE_STICKING_OUT), // 3 Smileys.getSmileyResource(Smileys.SURPRISED), // 4 Smileys.getSmileyResource(Smileys.KISSING), // 5 Smileys.getSmileyResource(Smileys.YELLING), // 6 Smileys.getSmileyResource(Smileys.COOL), // 7 Smileys.getSmileyResource(Smileys.MONEY_MOUTH), // 8 Smileys.getSmileyResource(Smileys.FOOT_IN_MOUTH), // 9 Smileys.getSmileyResource(Smileys.EMBARRASSED), // 10 Smileys.getSmileyResource(Smileys.ANGEL), // 11 Smileys.getSmileyResource(Smileys.UNDECIDED), // 12 Smileys.getSmileyResource(Smileys.CRYING), // 13 Smileys.getSmileyResource(Smileys.LIPS_ARE_SEALED), // 14 Smileys.getSmileyResource(Smileys.LAUGHING), // 15 Smileys.getSmileyResource(Smileys.WTF), // 16 Smileys.getSmileyResource(Smileys.MAD), // 17 Smileys.getSmileyResource(Smileys.HEART), // 18 Smileys.getSmileyResource(Smileys.SMIRK), // 19 Smileys.getSmileyResource(Smileys.POKERFACE), // 20 }; public static final int DEFAULT_SMILEY_TEXTS = R.array.default_smiley_texts; public static final int DEFAULT_SMILEY_NAMES = R.array.default_smiley_names; /** * Builds the hashtable we use for mapping the string version * of a smiley (e.g. ":-)") to a resource ID for the icon version. */ private HashMap<String, Integer> buildSmileyToRes() { if (DEFAULT_SMILEY_RES_IDS.length != mSmileyTexts.length) { // Throw an exception if someone updated DEFAULT_SMILEY_RES_IDS // and failed to update arrays.xml throw new IllegalStateException("Smiley resource ID/text mismatch"); } HashMap<String, Integer> smileyToRes = new HashMap<String, Integer>(mSmileyTexts.length); for (int i = 0; i < mSmileyTexts.length; i++) { smileyToRes.put(mSmileyTexts[i], DEFAULT_SMILEY_RES_IDS[i]); } return smileyToRes; } /** * Builds the regular expression we use to find smileys in {@link #addSmileySpans}. */ private Pattern buildPattern() { // Set the StringBuilder capacity with the assumption that the average // smiley is 3 characters long. StringBuilder patternString = new StringBuilder(mSmileyTexts.length * 3); // Build a regex that looks like (:-)|:-(|...), but escaping the smilies // properly so they will be interpreted literally by the regex matcher. patternString.append('('); for (String s : mSmileyTexts) { patternString.append(Pattern.quote(s)); patternString.append('|'); } // Replace the extra '|' with a ')' patternString.replace(patternString.length() - 1, patternString.length(), ")"); return Pattern.compile(patternString.toString()); } /** * Adds ImageSpans to a CharSequence that replace textual emoticons such * as :-) with a graphical version. * * @param text A CharSequence possibly containing emoticons * @return A CharSequence annotated with ImageSpans covering any * recognized emoticons. */ public CharSequence addSmileySpans(CharSequence text) { SpannableStringBuilder builder = new SpannableStringBuilder(text); Matcher matcher = mPattern.matcher(text); while (matcher.find()) { int resId = mSmileyToRes.get(matcher.group()); builder.setSpan(new ImageSpan(mContext, resId), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return builder; } }