package com.tomclaw.mandarin.util; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ImageSpan; import android.util.SparseArray; import com.tomclaw.mandarin.R; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created with IntelliJ IDEA. * User: Solkin * Date: 02.10.13 * Time: 23:02 * A class for annotating a CharSequence with spans to convert textual emoticons * to graphical ones. */ public class SmileyParser { @SuppressLint("StaticFieldLeak") private static SmileyParser instance = null; public static SmileyParser getInstance() { return instance; } public static void init(Context context) { if (instance == null) { instance = new SmileyParser(context); } } private static final int DEFAULT_SMILEY_TEXTS = R.array.default_smiley_texts; private static final int DEFAULT_SMILEY_IMAGES = R.array.default_smileys_images; private final Context context; private final String[] smileyTexts; private final TypedArray smileyDrawables; private final Pattern pattern; private final HashMap<String, Integer> smileyToRes; private final SparseArray<String> resToSmileys; private SmileyParser(Context context) { this.context = context; smileyTexts = this.context.getResources().getStringArray(DEFAULT_SMILEY_TEXTS); smileyDrawables = this.context.getResources().obtainTypedArray(DEFAULT_SMILEY_IMAGES); smileyToRes = new HashMap<>(); resToSmileys = new SparseArray<>(); buildSmileys(); pattern = buildPattern(); } /** * Builds the hashtable we use for mapping the string version * of a smiley (e.g. ":-)") to a resource ID for the icon version. */ private void buildSmileys() { if (smileyDrawables.length() != smileyTexts.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"); } for (int i = 0; i < smileyTexts.length; i++) { int resourceId = smileyDrawables.getResourceId(i, 0); smileyToRes.put(smileyTexts[i], resourceId); if (TextUtils.isEmpty(resToSmileys.get(resourceId))) { resToSmileys.put(resourceId, smileyTexts[i]); } } } /** * 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(smileyTexts.length * 3); // Build a regex that looks like (:-)|:-(|...), but escaping the smileys // properly so they will be interpreted literally by the regex matcher. patternString.append('('); for (String s : smileyTexts) { 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) { return addSmileySpans(new SpannableStringBuilder(text)); } /** * Adds ImageSpans to a Spannable that replace textual emoticons such * as :-) with a graphical version. * * @param text A Spannable possibly containing emoticons * @return A Spannable annotated with ImageSpans covering any * recognized emoticons. */ public Spannable addSmileySpans(Spannable text) { ImageSpan[] spans = text.getSpans(0, text.length(), ImageSpan.class); for (ImageSpan span : spans) { text.removeSpan(span); } Matcher matcher = pattern.matcher(text); while (matcher.find()) { int resId = smileyToRes.get(matcher.group()); text.setSpan(new ImageSpan(context, resId), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return text; } public int getSmileysCount() { return resToSmileys.size(); } public int getSmiley(int index) { return resToSmileys.keyAt(index); } public String getSmileyText(int index) { return resToSmileys.valueAt(index); } }