Home | History | Annotate | Download | only in util
      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 java.util.HashMap;
     20 import java.util.regex.Matcher;
     21 import java.util.regex.Pattern;
     22 
     23 import android.content.Context;
     24 import android.text.Spannable;
     25 import android.text.SpannableStringBuilder;
     26 import android.text.style.ImageSpan;
     27 
     28 import com.android.mms.R;
     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