/** * ************************************************************** getView the overridden getView * method */ public View getView(int position, View convertView, ViewGroup parent) { // update the text accordingly File f = new File(getItem(position)); int imageid; if (f.isDirectory()) imageid = R.drawable.fileicon_folder; else { String ext = getFileExtension(getItem(position)); if (ext.equals("c")) imageid = R.drawable.fileicon_c; else if (ext.equals("cpp")) imageid = R.drawable.fileicon_cpp; else if (ext.equals("f")) imageid = R.drawable.fileicon_f; else if (ext.equals("h")) imageid = R.drawable.fileicon_h; else if (ext.equals("htm")) imageid = R.drawable.fileicon_htm; else if (ext.equals("html")) imageid = R.drawable.fileicon_html; else if (ext.equals("java")) imageid = R.drawable.fileicon_java; else if (ext.equals("pl")) imageid = R.drawable.fileicon_pl; else if (ext.equals("py")) imageid = R.drawable.fileicon_py; else if (ext.equals("tex")) imageid = R.drawable.fileicon_tex; else if (ext.equals("txt")) imageid = R.drawable.fileicon_txt; else imageid = R.drawable.fileicon_default; } // This really speeds things up. Because only the number of views displayed at one time // are actually inflated View textEntryView; if (convertView != null) textEntryView = convertView; else textEntryView = factory.inflate(R.layout.filebrowser_item, null); TextView tv = (TextView) textEntryView.findViewById(R.id.itemtext); tv.setText(getItem(position)); ImageView iv = (ImageView) textEntryView.findViewById(R.id.itemimage); iv.setImageResource(imageid); if (mode == FILE_BROWSER_MODE) { Spannable text = sf.newSpannable(f.getName() + formatFileSize(f)); text.setSpan( new AbsoluteSizeSpan(20), 0, f.getName().length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); tv.setText(text); } else { // default to RECENT_FILE_MODE Spannable text; if (f.getParent().toString().equals("/")) text = sf.newSpannable(f.getName() + "\n/"); else text = sf.newSpannable(f.getName() + "\n" + f.getParent() + "/"); text.setSpan( new AbsoluteSizeSpan(20), 0, f.getName().length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); tv.setText(text); } return textEntryView; } // end getView()
public Spannable processEmojiMutable(CharSequence s, int mode) { long prev = 0; long prevLong = 0; int prevLongCount = 0; ArrayList<SpanDescription> list = new ArrayList<SpanDescription>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (Arrays.binarySearch(EMOJI_SORTED, prevId) >= 0) { list.add(new SpanDescription(prevId, i - 3, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (Arrays.binarySearch(EMOJI_SORTED, prevId) >= 0) { list.add(new SpanDescription(prevId, i - 1, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (Arrays.binarySearch(EMOJI_SORTED, current) >= 0) { list.add(new SpanDescription(current, i, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } Spannable spannable = spannableFactory.newSpannable(s); for (SpanDescription description : list) { spannable.setSpan( new EmojiSpan( this, indexes.get(description.id), emojiSideSize, originalMetrics.get(mode)), description.start, description.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannable; }
/** * 清除textview链接的下划线 * * @param textView */ public static void removeLinkUnderline(TextView textView) { Spannable s = Spannable.Factory.getInstance().newSpannable(textView.getText()); URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class); for (URLSpan span : spans) { int start = s.getSpanStart(span); int end = s.getSpanEnd(span); s.removeSpan(span); span = new TweetURLSpan(span.getURL()); s.setSpan(span, start, end, 0); } textView.setText(s); }
public static CharSequence getHtmlText(String text) { // fixes an android bug (?): text layout fails on text with nested style tags text = removeNestedTags(text, new String[] {"i", "b", "strong"}); final Spanned htmlText = Html.fromHtml(text); if (htmlText.getSpans(0, htmlText.length(), URLSpan.class).length == 0) { return htmlText; } final Spannable newHtmlText = Spannable.Factory.getInstance().newSpannable(htmlText); for (URLSpan span : newHtmlText.getSpans(0, newHtmlText.length(), URLSpan.class)) { final int start = newHtmlText.getSpanStart(span); final int end = newHtmlText.getSpanEnd(span); final int flags = newHtmlText.getSpanFlags(span); final String url = NetworkLibrary.Instance().rewriteUrl(span.getURL(), true); newHtmlText.removeSpan(span); newHtmlText.setSpan(new URLSpan(url), start, end, flags); } return newHtmlText; }
public class TimeZonePickerUtils { private static final String TAG = "TimeZonePickerUtils"; public static final int GMT_TEXT_COLOR = 0xFF888888; public static final int DST_SYMBOL_COLOR = 0xFFBFBFBF; private static final Factory mSpannableFactory = Spannable.Factory.getInstance(); private Locale mDefaultLocale; private String[] mOverrideIds; private String[] mOverrideLabels; /** * This needs to be an instantiated class so that it doesn't need to continuously re-load the list * of timezone IDs that need to be overridden. */ public TimeZonePickerUtils(Context context) { // Instead of saving a reference to the context (because we might need to look up the // labels every time getGmtDisplayName is called), we'll cache the lists of override IDs // and labels now. cacheOverrides(context); } /** * Given a timezone id (e.g. America/Los_Angeles), returns the corresponding timezone display name * (e.g. Pacific Time GMT-7). * * @param context Context in case the override labels need to be re-cached. * @param id The timezone id * @param millis The time (daylight savings or not) * @param grayGmt Whether the "GMT+x" part of the returned string should be colored gray. * @return The display name of the timezone. */ public CharSequence getGmtDisplayName(Context context, String id, long millis, boolean grayGmt) { TimeZone timezone = TimeZone.getTimeZone(id); if (timezone == null) { return null; } final Locale defaultLocale = Locale.getDefault(); if (!defaultLocale.equals(mDefaultLocale)) { // If the IDs and labels haven't been set yet, or if the locale has been changed // recently, we'll need to re-cache them. mDefaultLocale = defaultLocale; cacheOverrides(context); } return buildGmtDisplayName(timezone, millis, grayGmt); } private CharSequence buildGmtDisplayName(TimeZone tz, long timeMillis, boolean grayGmt) { Time time = new Time(tz.getID()); time.set(timeMillis); StringBuilder sb = new StringBuilder(); String displayName = getDisplayName(tz, time.isDst != 0); sb.append(displayName); sb.append(" "); final int gmtOffset = tz.getOffset(timeMillis); int gmtStart = sb.length(); appendGmtOffset(sb, gmtOffset); int gmtEnd = sb.length(); int symbolStart = 0; int symbolEnd = 0; if (tz.useDaylightTime()) { sb.append(" "); symbolStart = sb.length(); sb.append(getDstSymbol()); // Sun symbol symbolEnd = sb.length(); } // Set the gray colors. Spannable spannableText = mSpannableFactory.newSpannable(sb); if (grayGmt) { spannableText.setSpan( new ForegroundColorSpan(GMT_TEXT_COLOR), gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } if (tz.useDaylightTime()) { spannableText.setSpan( new ForegroundColorSpan(DST_SYMBOL_COLOR), symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } CharSequence gmtDisplayName = spannableText; return gmtDisplayName; } public static void appendGmtOffset(StringBuilder sb, final int gmtOffset) { sb.append("GMT"); if (gmtOffset < 0) { sb.append('-'); } else { sb.append('+'); } final int p = Math.abs(gmtOffset); sb.append(p / DateUtils.HOUR_IN_MILLIS); // Hour final int min = (p / (int) DateUtils.MINUTE_IN_MILLIS) % 60; if (min != 0) { // Show minutes if non-zero sb.append(':'); if (min < 10) { sb.append('0'); } sb.append(min); } } public static char getDstSymbol() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { return '\u2600'; // The Sun emoji icon. } else { return '*'; } } /** * Gets the display name for the specified Timezone ID. If the ID matches the list of IDs that * need to be have their default display names overriden, use the pre-set display name from * R.arrays. * * @param id The timezone ID. * @param daylightTime True for daylight time, false for standard time * @return The display name of the timezone. This will just use the default display name, except * that certain timezones have poor defaults, and should use the pre-set override labels from * R.arrays. */ private String getDisplayName(TimeZone tz, boolean daylightTime) { if (mOverrideIds == null || mOverrideLabels == null) { // Just in case they somehow didn't get loaded correctly. return tz.getDisplayName(daylightTime, TimeZone.LONG, Locale.getDefault()); } for (int i = 0; i < mOverrideIds.length; i++) { if (tz.getID().equals(mOverrideIds[i])) { if (mOverrideLabels.length > i) { return mOverrideLabels[i]; } Log.e( TAG, "timezone_rename_ids len=" + mOverrideIds.length + " timezone_rename_labels len=" + mOverrideLabels.length); break; } } // If the ID doesn't need to have the display name overridden, or if the labels were // malformed, just use the default. return tz.getDisplayName(daylightTime, TimeZone.LONG, Locale.getDefault()); } private void cacheOverrides(Context context) { Resources res = context.getResources(); mOverrideIds = res.getStringArray(R.array.timezone_rename_ids); mOverrideLabels = res.getStringArray(R.array.timezone_rename_labels); } }
public Spannable getTextWithImages(Context context, CharSequence text) { Spannable spannable = Spannable.Factory.getInstance().newSpannable(text); addImages(context, spannable); return spannable; }
public class SmileUtils { public static final String ee_1 = "[):]"; public static final String ee_2 = "[:D]"; public static final String ee_3 = "[;)]"; public static final String ee_4 = "[:-o]"; public static final String ee_5 = "[:p]"; public static final String ee_6 = "[(H)]"; public static final String ee_7 = "[:@]"; public static final String ee_8 = "[:s]"; public static final String ee_9 = "[:$]"; public static final String ee_10 = "[:(]"; public static final String ee_11 = "[:'(]"; public static final String ee_12 = "[:|]"; public static final String ee_13 = "[(a)]"; public static final String ee_14 = "[8o|]"; public static final String ee_15 = "[8-|]"; public static final String ee_16 = "[+o(]"; public static final String ee_17 = "[<o)]"; public static final String ee_18 = "[|-)]"; public static final String ee_19 = "[*-)]"; public static final String ee_20 = "[:-#]"; public static final String ee_21 = "[:-*]"; public static final String ee_22 = "[^o)]"; public static final String ee_23 = "[8-)]"; public static final String ee_24 = "[(|)]"; public static final String ee_25 = "[(u)]"; public static final String ee_26 = "[(S)]"; public static final String ee_27 = "[(*)]"; public static final String ee_28 = "[(#)]"; public static final String ee_29 = "[(R)]"; public static final String ee_30 = "[({)]"; public static final String ee_31 = "[(})]"; public static final String ee_32 = "[(k)]"; public static final String ee_33 = "[(F)]"; public static final String ee_34 = "[(W)]"; public static final String ee_35 = "[(D)]"; private static final Factory spannableFactory = Spannable.Factory.getInstance(); private static final Map<Pattern, Integer> emoticons = new HashMap<Pattern, Integer>(); static { addPattern(emoticons, ee_1, R.drawable.ee_1); addPattern(emoticons, ee_2, R.drawable.ee_2); addPattern(emoticons, ee_3, R.drawable.ee_3); addPattern(emoticons, ee_4, R.drawable.ee_4); addPattern(emoticons, ee_5, R.drawable.ee_5); addPattern(emoticons, ee_6, R.drawable.ee_6); addPattern(emoticons, ee_7, R.drawable.ee_7); addPattern(emoticons, ee_8, R.drawable.ee_8); addPattern(emoticons, ee_9, R.drawable.ee_9); addPattern(emoticons, ee_10, R.drawable.ee_10); addPattern(emoticons, ee_11, R.drawable.ee_11); addPattern(emoticons, ee_12, R.drawable.ee_12); addPattern(emoticons, ee_13, R.drawable.ee_13); addPattern(emoticons, ee_14, R.drawable.ee_14); addPattern(emoticons, ee_15, R.drawable.ee_15); addPattern(emoticons, ee_16, R.drawable.ee_16); addPattern(emoticons, ee_17, R.drawable.ee_17); addPattern(emoticons, ee_18, R.drawable.ee_18); addPattern(emoticons, ee_19, R.drawable.ee_19); addPattern(emoticons, ee_20, R.drawable.ee_20); addPattern(emoticons, ee_21, R.drawable.ee_21); addPattern(emoticons, ee_22, R.drawable.ee_22); addPattern(emoticons, ee_23, R.drawable.ee_23); addPattern(emoticons, ee_24, R.drawable.ee_24); addPattern(emoticons, ee_25, R.drawable.ee_25); addPattern(emoticons, ee_26, R.drawable.ee_26); addPattern(emoticons, ee_27, R.drawable.ee_27); addPattern(emoticons, ee_28, R.drawable.ee_28); addPattern(emoticons, ee_29, R.drawable.ee_29); addPattern(emoticons, ee_30, R.drawable.ee_30); addPattern(emoticons, ee_31, R.drawable.ee_31); addPattern(emoticons, ee_32, R.drawable.ee_32); addPattern(emoticons, ee_33, R.drawable.ee_33); addPattern(emoticons, ee_34, R.drawable.ee_34); addPattern(emoticons, ee_35, R.drawable.ee_35); } private static void addPattern(Map<Pattern, Integer> map, String smile, int resource) { map.put(Pattern.compile(Pattern.quote(smile)), resource); } /** * replace existing spannable with smiles * * @param context * @param spannable * @return */ public static boolean addSmiles(Context context, Spannable spannable) { boolean hasChanges = false; for (Entry<Pattern, Integer> entry : emoticons.entrySet()) { Matcher matcher = entry.getKey().matcher(spannable); while (matcher.find()) { boolean set = true; for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) if (spannable.getSpanStart(span) >= matcher.start() && spannable.getSpanEnd(span) <= matcher.end()) spannable.removeSpan(span); else { set = false; break; } if (set) { hasChanges = true; spannable.setSpan( new ImageSpan(context, entry.getValue()), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } return hasChanges; } public static Spannable getSmiledText(Context context, CharSequence text) { Spannable spannable = spannableFactory.newSpannable(text); addSmiles(context, spannable); return spannable; } public static boolean containsKey(String key) { boolean b = false; for (Entry<Pattern, Integer> entry : emoticons.entrySet()) { Matcher matcher = entry.getKey().matcher(key); if (matcher.find()) { b = true; break; } } return b; } }
public class SmileProcessor { private static final String TAG = "Emoji"; private static final int COUNT_IN_ROW = 32; private static final int COUNT_IN_COL = 26; private static final int SECTION_SIDE = 8; private static final int SECTION_ROW_COUNT = COUNT_IN_ROW / SECTION_SIDE; private static final int SECTION_COL_COUNT = (int) Math.ceil((float) COUNT_IN_COL / SECTION_SIDE); public static final int CONFIGURATION_DIALOGS = 0; public static final int CONFIGURATION_BUBBLES = 1; private static final Long[] EMOJI_MAP = { 2302179L, 3154147L, 3219683L, 3285219L, 3350755L, 3416291L, 3481827L, 3547363L, 3612899L, 3678435L, 3743971L, 169L, 174L, 8252L, 8265L, 8482L, 8505L, 8596L, 8597L, 8598L, 8599L, 8600L, 8601L, 8617L, 8618L, 8986L, 8987L, 9193L, 9194L, 9195L, 9196L, 9200L, 9203L, 9410L, 9642L, 9643L, 9654L, 9664L, 9723L, 9724L, 9725L, 9726L, 9728L, 9729L, 9742L, 9745L, 9748L, 9749L, 9757L, 9786L, 9800L, 9801L, 9802L, 9803L, 9804L, 9805L, 9806L, 9807L, 9808L, 9809L, 9810L, 9811L, 9824L, 9827L, 9829L, 9830L, 9832L, 9851L, 9855L, 9875L, 9888L, 9889L, 9898L, 9899L, 9917L, 9918L, 9924L, 9925L, 9934L, 9940L, 9962L, 9970L, 9971L, 9973L, 9978L, 9981L, 9986L, 9989L, 9992L, 9993L, 9994L, 9995L, 9996L, 9999L, 10002L, 10004L, 10006L, 10024L, 10035L, 10036L, 10052L, 10055L, 10060L, 10062L, 10067L, 10068L, 10069L, 10071L, 10084L, 10133L, 10134L, 10135L, 10145L, 10160L, 10175L, 10548L, 10549L, 11013L, 11014L, 11015L, 11035L, 11036L, 11088L, 11093L, 12336L, 12349L, 12951L, 12953L, 3627867140L, 3627867343L, 3627867504L, 3627867505L, 3627867518L, 3627867519L, 3627867534L, 3627867537L, 3627867538L, 3627867539L, 3627867540L, 3627867541L, 3627867542L, 3627867543L, 3627867544L, 3627867545L, 3627867546L, -2865171270784459277L, -2865171266489491990L, -2865171262194524680L, -2865171257899557385L, -2865171253604590105L, -2865171245014655495L, -2865171240719688203L, -2865171236424720905L, -2865171206359949830L, -2865171193475047944L, 3627867649L, 3627867650L, 3627867674L, 3627867695L, 3627867699L, 3627867701L, 3627867702L, 3627867703L, 3627867704L, 3627867705L, 3627867706L, 3627867728L, 3627867904L, 3627867905L, 3627867906L, 3627867907L, 3627867908L, 3627867909L, 3627867910L, 3627867911L, 3627867912L, 3627867913L, 3627867914L, 3627867915L, 3627867916L, 3627867917L, 3627867918L, 3627867919L, 3627867920L, 3627867921L, 3627867922L, 3627867923L, 3627867924L, 3627867925L, 3627867926L, 3627867927L, 3627867928L, 3627867929L, 3627867930L, 3627867931L, 3627867932L, 3627867933L, 3627867934L, 3627867935L, 3627867936L, 3627867952L, 3627867953L, 3627867954L, 3627867955L, 3627867956L, 3627867957L, 3627867959L, 3627867960L, 3627867961L, 3627867962L, 3627867963L, 3627867964L, 3627867965L, 3627867966L, 3627867967L, 3627867968L, 3627867969L, 3627867970L, 3627867971L, 3627867972L, 3627867973L, 3627867974L, 3627867975L, 3627867976L, 3627867977L, 3627867978L, 3627867979L, 3627867980L, 3627867981L, 3627867982L, 3627867983L, 3627867984L, 3627867985L, 3627867986L, 3627867987L, 3627867988L, 3627867989L, 3627867990L, 3627867991L, 3627867992L, 3627867993L, 3627867994L, 3627867995L, 3627867996L, 3627867997L, 3627867998L, 3627867999L, 3627868000L, 3627868001L, 3627868002L, 3627868003L, 3627868004L, 3627868005L, 3627868006L, 3627868007L, 3627868008L, 3627868009L, 3627868010L, 3627868011L, 3627868012L, 3627868013L, 3627868014L, 3627868015L, 3627868016L, 3627868017L, 3627868018L, 3627868019L, 3627868020L, 3627868021L, 3627868022L, 3627868023L, 3627868024L, 3627868025L, 3627868026L, 3627868027L, 3627868028L, 3627868032L, 3627868033L, 3627868034L, 3627868035L, 3627868036L, 3627868037L, 3627868038L, 3627868039L, 3627868040L, 3627868041L, 3627868042L, 3627868043L, 3627868044L, 3627868045L, 3627868046L, 3627868047L, 3627868048L, 3627868049L, 3627868050L, 3627868051L, 3627868064L, 3627868065L, 3627868066L, 3627868067L, 3627868068L, 3627868069L, 3627868070L, 3627868071L, 3627868072L, 3627868073L, 3627868074L, 3627868075L, 3627868076L, 3627868077L, 3627868078L, 3627868079L, 3627868080L, 3627868081L, 3627868082L, 3627868083L, 3627868084L, 3627868085L, 3627868086L, 3627868087L, 3627868088L, 3627868089L, 3627868090L, 3627868091L, 3627868092L, 3627868093L, 3627868094L, 3627868095L, 3627868096L, 3627868097L, 3627868098L, 3627868099L, 3627868100L, 3627868102L, 3627868103L, 3627868104L, 3627868105L, 3627868106L, 3627868128L, 3627868129L, 3627868130L, 3627868131L, 3627868132L, 3627868133L, 3627868134L, 3627868135L, 3627868136L, 3627868137L, 3627868138L, 3627868139L, 3627868140L, 3627868141L, 3627868142L, 3627868143L, 3627868144L, 3627932672L, 3627932673L, 3627932674L, 3627932675L, 3627932676L, 3627932677L, 3627932678L, 3627932679L, 3627932680L, 3627932681L, 3627932682L, 3627932683L, 3627932684L, 3627932685L, 3627932686L, 3627932687L, 3627932688L, 3627932689L, 3627932690L, 3627932691L, 3627932692L, 3627932693L, 3627932694L, 3627932695L, 3627932696L, 3627932697L, 3627932698L, 3627932699L, 3627932700L, 3627932701L, 3627932702L, 3627932703L, 3627932704L, 3627932705L, 3627932706L, 3627932707L, 3627932708L, 3627932709L, 3627932710L, 3627932711L, 3627932712L, 3627932713L, 3627932714L, 3627932715L, 3627932716L, 3627932717L, 3627932718L, 3627932719L, 3627932720L, 3627932721L, 3627932722L, 3627932723L, 3627932724L, 3627932725L, 3627932726L, 3627932727L, 3627932728L, 3627932729L, 3627932730L, 3627932731L, 3627932732L, 3627932733L, 3627932734L, 3627932736L, 3627932738L, 3627932739L, 3627932740L, 3627932741L, 3627932742L, 3627932743L, 3627932744L, 3627932745L, 3627932746L, 3627932747L, 3627932748L, 3627932749L, 3627932750L, 3627932751L, 3627932752L, 3627932753L, 3627932754L, 3627932755L, 3627932756L, 3627932757L, 3627932758L, 3627932759L, 3627932760L, 3627932761L, 3627932762L, 3627932763L, 3627932764L, 3627932765L, 3627932766L, 3627932767L, 3627932768L, 3627932769L, 3627932770L, 3627932771L, 3627932772L, 3627932773L, 3627932774L, 3627932775L, 3627932776L, 3627932777L, 3627932778L, 3627932779L, 3627932780L, 3627932781L, 3627932782L, 3627932783L, 3627932784L, 3627932785L, 3627932786L, 3627932787L, 3627932788L, 3627932789L, 3627932790L, 3627932791L, 3627932792L, 3627932793L, 3627932794L, 3627932795L, 3627932796L, 3627932797L, 3627932798L, 3627932799L, 3627932800L, 3627932801L, 3627932802L, 3627932803L, 3627932804L, 3627932805L, 3627932806L, 3627932807L, 3627932808L, 3627932809L, 3627932810L, 3627932811L, 3627932812L, 3627932813L, 3627932814L, 3627932815L, 3627932816L, 3627932817L, 3627932818L, 3627932819L, 3627932820L, 3627932821L, 3627932822L, 3627932823L, 3627932824L, 3627932825L, 3627932826L, 3627932827L, 3627932828L, 3627932829L, 3627932830L, 3627932831L, 3627932832L, 3627932833L, 3627932834L, 3627932835L, 3627932836L, 3627932837L, 3627932838L, 3627932839L, 3627932840L, 3627932841L, 3627932842L, 3627932843L, 3627932844L, 3627932845L, 3627932846L, 3627932847L, 3627932848L, 3627932849L, 3627932850L, 3627932851L, 3627932852L, 3627932853L, 3627932854L, 3627932855L, 3627932856L, 3627932857L, 3627932858L, 3627932859L, 3627932860L, 3627932861L, 3627932862L, 3627932863L, 3627932864L, 3627932865L, 3627932866L, 3627932867L, 3627932868L, 3627932869L, 3627932870L, 3627932871L, 3627932872L, 3627932873L, 3627932874L, 3627932875L, 3627932876L, 3627932877L, 3627932878L, 3627932879L, 3627932880L, 3627932881L, 3627932882L, 3627932883L, 3627932884L, 3627932885L, 3627932886L, 3627932887L, 3627932888L, 3627932889L, 3627932890L, 3627932891L, 3627932892L, 3627932893L, 3627932894L, 3627932895L, 3627932896L, 3627932897L, 3627932898L, 3627932899L, 3627932900L, 3627932901L, 3627932902L, 3627932903L, 3627932904L, 3627932905L, 3627932906L, 3627932907L, 3627932908L, 3627932909L, 3627932910L, 3627932911L, 3627932912L, 3627932913L, 3627932914L, 3627932915L, 3627932916L, 3627932917L, 3627932918L, 3627932919L, 3627932921L, 3627932922L, 3627932923L, 3627932924L, 3627932928L, 3627932929L, 3627932930L, 3627932931L, 3627932932L, 3627932933L, 3627932934L, 3627932935L, 3627932936L, 3627932937L, 3627932938L, 3627932939L, 3627932940L, 3627932941L, 3627932942L, 3627932943L, 3627932944L, 3627932945L, 3627932946L, 3627932947L, 3627932948L, 3627932949L, 3627932950L, 3627932951L, 3627932952L, 3627932953L, 3627932954L, 3627932955L, 3627932956L, 3627932957L, 3627932958L, 3627932959L, 3627932960L, 3627932961L, 3627932962L, 3627932963L, 3627932964L, 3627932965L, 3627932966L, 3627932967L, 3627932968L, 3627932969L, 3627932970L, 3627932971L, 3627932972L, 3627932973L, 3627932974L, 3627932975L, 3627932976L, 3627932977L, 3627932978L, 3627932979L, 3627932980L, 3627932981L, 3627932982L, 3627932983L, 3627932984L, 3627932985L, 3627932986L, 3627932987L, 3627932988L, 3627932989L, 3627933008L, 3627933009L, 3627933010L, 3627933011L, 3627933012L, 3627933013L, 3627933014L, 3627933015L, 3627933016L, 3627933017L, 3627933018L, 3627933019L, 3627933179L, 3627933180L, 3627933181L, 3627933182L, 3627933183L, 3627933184L, 3627933185L, 3627933186L, 3627933187L, 3627933188L, 3627933189L, 3627933190L, 3627933191L, 3627933192L, 3627933193L, 3627933194L, 3627933195L, 3627933196L, 3627933197L, 3627933198L, 3627933199L, 3627933200L, 3627933201L, 3627933202L, 3627933203L, 3627933204L, 3627933205L, 3627933206L, 3627933207L, 3627933208L, 3627933209L, 3627933210L, 3627933211L, 3627933212L, 3627933213L, 3627933214L, 3627933215L, 3627933216L, 3627933217L, 3627933218L, 3627933219L, 3627933220L, 3627933221L, 3627933222L, 3627933223L, 3627933224L, 3627933225L, 3627933226L, 3627933227L, 3627933228L, 3627933229L, 3627933230L, 3627933231L, 3627933232L, 3627933233L, 3627933234L, 3627933235L, 3627933236L, 3627933237L, 3627933238L, 3627933239L, 3627933240L, 3627933241L, 3627933242L, 3627933243L, 3627933244L, 3627933245L, 3627933246L, 3627933247L, 3627933248L, 3627933253L, 3627933254L, 3627933255L, 3627933256L, 3627933257L, 3627933258L, 3627933259L, 3627933260L, 3627933261L, 3627933262L, 3627933263L, 3627933312L, 3627933313L, 3627933314L, 3627933315L, 3627933316L, 3627933317L, 3627933318L, 3627933319L, 3627933320L, 3627933321L, 3627933322L, 3627933323L, 3627933324L, 3627933325L, 3627933326L, 3627933327L, 3627933328L, 3627933329L, 3627933330L, 3627933331L, 3627933332L, 3627933333L, 3627933334L, 3627933335L, 3627933336L, 3627933337L, 3627933338L, 3627933339L, 3627933340L, 3627933341L, 3627933342L, 3627933343L, 3627933344L, 3627933345L, 3627933346L, 3627933347L, 3627933348L, 3627933349L, 3627933350L, 3627933351L, 3627933352L, 3627933353L, 3627933354L, 3627933355L, 3627933356L, 3627933357L, 3627933358L, 3627933359L, 3627933360L, 3627933361L, 3627933362L, 3627933363L, 3627933364L, 3627933365L, 3627933366L, 3627933367L, 3627933368L, 3627933369L, 3627933370L, 3627933371L, 3627933372L, 3627933373L, 3627933374L, 3627933375L, 3627933376L, 3627933377L, 3627933378L, 3627933379L, 3627933380L, 3627933381L }; private static final long[] EMOJI_SORTED; private static final long minEmoji1; private static final long maxEmoji1; private static final long minEmoji2; private static final long maxEmoji2; private static final HashSet<Long> EMOJI_SET = new HashSet<Long>(); static { Collections.addAll(EMOJI_SET, EMOJI_MAP); EMOJI_SORTED = new long[EMOJI_MAP.length]; long min1 = 0xFFFF, max1 = 0; long min2 = 0xFFFFFFFF, max2 = 0; for (int i = 0; i < EMOJI_MAP.length; i++) { EMOJI_SORTED[i] = EMOJI_MAP[i]; if ((EMOJI_SORTED[i] & 0xffff) == EMOJI_SORTED[i]) { if (EMOJI_SORTED[i] < min1) { min1 = EMOJI_SORTED[i]; } if (EMOJI_SORTED[i] > max1) { max1 = EMOJI_SORTED[i]; } } else if ((EMOJI_SORTED[i] & 0xffffffff) == EMOJI_SORTED[i]) { if (EMOJI_SORTED[i] < min2) { min2 = EMOJI_SORTED[i]; } if (EMOJI_SORTED[i] > max2) { max2 = EMOJI_SORTED[i]; } } } minEmoji1 = min1; maxEmoji1 = max1; minEmoji2 = min2; maxEmoji2 = max2; Arrays.sort(EMOJI_SORTED); } private static final int LAYOUT_1X = 1; private static final int LAYOUT_15X_1 = 2; private static final int LAYOUT_15X_2 = 3; private static final int LAYOUT_2X_1 = 4; private static final int LAYOUT_2X_2 = 5; private static SmileProcessor instance; private static SmileProcessor processor; // protected Bitmap emojiImages; protected HashMap<Integer, Bitmap> emojiMap; private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance(); private Application application; private float density; private HashMap<Long, Integer> indexes; private HashMap<Integer, Paint.FontMetricsInt> originalMetrics; private int layoutType = LAYOUT_1X; private boolean isLoading = false; private boolean isLoaded = false; private Handler handler = new Handler(Looper.getMainLooper()); private CopyOnWriteArrayList<SmilesListener> listeners = new CopyOnWriteArrayList<SmilesListener>(); private int emojiSideSize; private int rectSize = 0; private SmilesRecentListener smilesRecentListener; private SmilesRecentsController recentController; public static final SmileProcessor emoji() { return processor; } public SmileProcessor(Application application) { long start = System.currentTimeMillis(); this.application = application; processor = this; density = application.getResources().getDisplayMetrics().density; emojiSideSize = (int) (density * 20); Logger.d(TAG, "Emoji phase 0 in " + (System.currentTimeMillis() - start) + " ms"); if (density >= 2 || density == 1) { if (density >= 2) { // XHDPI and more if (SmileysPack.PACK_2) { layoutType = LAYOUT_2X_1; } else if (SmileysPack.PACK_15) { layoutType = LAYOUT_15X_1; } else if (SmileysPack.PACK_1) { layoutType = LAYOUT_1X; } else { throw new RuntimeException("Unable to find smileys pack"); } } else { // MDPI if (SmileysPack.PACK_1) { layoutType = LAYOUT_1X; } else if (SmileysPack.PACK_15) { layoutType = LAYOUT_15X_1; } else if (SmileysPack.PACK_2) { layoutType = LAYOUT_2X_2; } else { throw new RuntimeException("Unable to find smileys pack"); } } } else { if (density > 1) { // 1.3333 and 1.5 // HDPI & TVDPI if (SmileysPack.PACK_15) { layoutType = LAYOUT_15X_1; } else if (SmileysPack.PACK_2) { layoutType = LAYOUT_2X_1; } else if (SmileysPack.PACK_1) { layoutType = LAYOUT_1X; } else { throw new RuntimeException("Unable to find smileys pack"); } } else { // 0.75 // LDPI if (SmileysPack.PACK_15) { layoutType = LAYOUT_15X_2; } else if (SmileysPack.PACK_1) { layoutType = LAYOUT_1X; } else if (SmileysPack.PACK_2) { layoutType = LAYOUT_2X_2; } else { throw new RuntimeException("Unable to find smileys pack"); } } } Logger.d(TAG, "Emoji phase 1 in " + (System.currentTimeMillis() - start) + " ms"); start = System.currentTimeMillis(); switch (layoutType) { default: case LAYOUT_1X: rectSize = 28; break; case LAYOUT_15X_1: rectSize = 36; break; case LAYOUT_15X_2: rectSize = 18; break; case LAYOUT_2X_1: rectSize = 56; break; case LAYOUT_2X_2: rectSize = 28; break; } indexes = new HashMap<Long, Integer>(); emojiMap = new HashMap<Integer, Bitmap>(); originalMetrics = new HashMap<Integer, Paint.FontMetricsInt>(); TextPaint bodyPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG); // bodyPaint.setTypeface(FontController.loadTypeface(application, "normal")); bodyPaint.setTextSize(getSp(16)); bodyPaint.setColor(0xff000000); originalMetrics.put(CONFIGURATION_BUBBLES, bodyPaint.getFontMetricsInt()); bodyPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG); // bodyPaint.setTypeface(FontController.loadTypeface(application, "light")); bodyPaint.setColor(0xff808080); bodyPaint.setTextSize(getSp(15.5f)); originalMetrics.put(CONFIGURATION_DIALOGS, bodyPaint.getFontMetricsInt()); Logger.d(TAG, "Emoji phase 2 in " + (System.currentTimeMillis() - start) + " ms"); start = System.currentTimeMillis(); for (int i = 0; i < EMOJI_MAP.length; i++) { indexes.put(EMOJI_MAP[i], i); } Logger.d(TAG, "Emoji phase 3 in " + (System.currentTimeMillis() - start) + " ms"); } protected int getSp(float sp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, sp, application.getResources().getDisplayMetrics()); } public boolean isLoaded() { return isLoaded; } public void registerListener(SmilesListener listener) { if (!listeners.contains(listener)) { listeners.add(listener); } } public void unregisterListener(SmilesListener listener) { listeners.remove(listener); } public void loadEmoji() { if (isLoaded) { return; } if (isLoading) { return; } isLoading = true; new Thread() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); long start = System.currentTimeMillis(); Logger.d(TAG, "emoji loading start"); try { boolean useScale = false; String fileName = null; String fileNameAlpha = null; switch (layoutType) { default: case LAYOUT_1X: fileName = "emoji_c_1.jpg"; fileNameAlpha = "emoji_a_1.jpg"; useScale = false; break; case LAYOUT_15X_1: fileName = "emoji_c_15.jpg"; fileNameAlpha = "emoji_a_15.jpg"; useScale = false; break; case LAYOUT_15X_2: fileName = "emoji_c_15.jpg"; fileNameAlpha = "emoji_a_15.jpg"; useScale = true; break; case LAYOUT_2X_1: fileName = "emoji_c_2.jpg"; fileNameAlpha = "emoji_a_2.jpg"; useScale = false; break; case LAYOUT_2X_2: fileName = "emoji_c_2.jpg"; fileNameAlpha = "emoji_a_2.jpg"; useScale = true; break; } File sourceFile = application.getFileStreamPath(fileName); if (!sourceFile.exists()) { InputStream colorsIs = SmileProcessor.this.application.getAssets().open(fileName); IOUtils.copy(colorsIs, sourceFile); colorsIs.close(); } File sourceAlphaFile = application.getFileStreamPath(fileNameAlpha); if (!sourceAlphaFile.exists()) { InputStream colorsIs = SmileProcessor.this.application.getAssets().open(fileNameAlpha); IOUtils.copy(colorsIs, sourceAlphaFile); colorsIs.close(); } ImageMetadata metadata = new FileSource(sourceFile.getAbsolutePath()).getImageMetadata(); int w = useScale ? metadata.getW() / 2 : metadata.getW(); int h = useScale ? metadata.getH() / 2 : metadata.getH(); Bitmap colorsBitmap; Bitmap alphaBitmap; if (useScale) { colorsBitmap = ImageLoading.loadBitmap(sourceFile.getAbsolutePath(), 2); alphaBitmap = ImageLoading.loadBitmap(sourceAlphaFile.getAbsolutePath(), 2); } else { colorsBitmap = ImageLoading.loadBitmap(sourceFile.getAbsolutePath(), 1); alphaBitmap = ImageLoading.loadBitmap(sourceAlphaFile.getAbsolutePath(), 1); } // Bitmap colorsBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); // BitmapDecoderEx.decodeReuseBitmapScaled(sourceFile.getAbsolutePath(), colorsBitmap); // colorsBitmap.setHasAlpha(true); // BitmapDecoderEx.decodeReuseBitmapBlend(sourceAlphaFile.getAbsolutePath(), colorsBitmap, // useScale); Logger.d(TAG, "emoji pre-loaded in " + (System.currentTimeMillis() - start) + " ms"); int[] resultColors = new int[rectSize * SECTION_SIDE * rectSize * SECTION_SIDE]; int[] tmpColors = new int[rectSize * SECTION_SIDE * rectSize * SECTION_SIDE]; int[] order = new int[] {8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15}; int stride = rectSize * SECTION_SIDE; for (int ordinal : order) { int col = ordinal % SECTION_COL_COUNT; int row = ordinal / SECTION_COL_COUNT; int leftOffset = col * stride; int topOffset = row * stride; int width = stride; int height = stride; if (row == SECTION_ROW_COUNT - 1) { height = colorsBitmap.getHeight() - topOffset; } colorsBitmap.getPixels(tmpColors, 0, stride, leftOffset, topOffset, width, height); for (int ind = 0; ind < resultColors.length; ind++) { resultColors[ind] = 0xFFFFFF & tmpColors[ind]; } alphaBitmap.getPixels(tmpColors, 0, stride, leftOffset, topOffset, width, height); for (int ind = 0; ind < resultColors.length; ind++) { resultColors[ind] = resultColors[ind] | ((tmpColors[ind] & 0xFF) << 24); } Bitmap section = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); /*Canvas canvas = new Canvas(section); canvas.drawBitmap(colorsBitmap, new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height), new Rect(0, 0, width, height), new Paint());*/ section.setPixels(resultColors, 0, stride, 0, 0, width, height); emojiMap.put(ordinal, section); Logger.d(TAG, "emoji region loaded in " + (System.currentTimeMillis() - start) + " ms"); } recentController = SmilesRecentsController.getInstance(application); isLoaded = true; notifyEmojiUpdated(true); Logger.d(TAG, "emoji loaded in " + (System.currentTimeMillis() - start) + " ms"); } catch (Throwable t) { t.printStackTrace(); Logger.d(TAG, "emoji loading error"); isLoaded = false; isLoading = false; } } }.start(); } private void notifyEmojiUpdated(final boolean completed) { handler.post( new Runnable() { @Override public void run() { Logger.d(TAG, "notify"); for (SmilesListener listener : listeners) { listener.onSmilesUpdated(completed); } } }); } public void waitForEmoji() { if (isLoaded) { return; } final Object lock = new Object(); synchronized (lock) { listeners.add( new SmilesListener() { @Override public void onSmilesUpdated(boolean completed) { lock.notify(); } }); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); return; } } } private long getId(String val) { long id = 0; if (val.length() == 1) { id = val.charAt(0); } else { id = ((long) val.charAt(0) << 16) + (long) val.charAt(1); } return id; } public Bitmap getBitmap(String emojiString) { return getSection(emojiString.charAt(0)); } public void upRecent(long smileId) { SmilesPack.upRecent(smileId); if (smilesRecentListener != null) { smilesRecentListener.onSmilesUpdated(); } } public void setRecentUpdateListener(SmilesRecentListener smilesRecentListener) { this.smilesRecentListener = smilesRecentListener; } public SmilesRecentsController getRecentController() { return recentController; } private class SpanDescription { private SpanDescription(long id, int start, int end) { this.id = id; this.start = start; this.end = end; } public long id; public int start, end; } public String cutEmoji(String s) { StringBuilder stringBuilder = new StringBuilder(); long prev = 0; long prevLong = 0; int prevLongCount = 0; int lastTextPos = 0; ArrayList<SpanDescription> list = new ArrayList<SpanDescription>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 3) { stringBuilder.append(s.substring(lastTextPos, i - 3)); lastTextPos = i - 3; } stringBuilder.append(":smile:"); lastTextPos += 4; list.add(new SpanDescription(prevId, i - 3, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 1) { stringBuilder.append(s.substring(lastTextPos, i - 1)); lastTextPos = i - 1; } stringBuilder.append(":smile:"); lastTextPos += 2; list.add(new SpanDescription(prevId, i - 1, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (EMOJI_SET.contains(current)) { if (lastTextPos < i) { stringBuilder.append(s.substring(lastTextPos, i)); lastTextPos = i; } stringBuilder.append(":smile:"); lastTextPos += 1; list.add(new SpanDescription(current, i, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } if (lastTextPos < s.length()) { stringBuilder.append(s.substring(lastTextPos, s.length())); } return stringBuilder.toString(); } public String fixStringCompat(String src) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { return src; } else { return cutEmoji(src); } } public Spannable processEmojiCutMutable(String s, int mode) { StringBuilder stringBuilder = new StringBuilder(); long prev = 0; long prevLong = 0; int prevLongCount = 0; int lastTextPos = 0; ArrayList<SpanDescription> list = new ArrayList<SpanDescription>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 3) { stringBuilder.append(s.substring(lastTextPos, i - 3)); lastTextPos = i - 3; } stringBuilder.append("++++"); lastTextPos += 4; list.add(new SpanDescription(prevId, i - 3, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 1) { stringBuilder.append(s.substring(lastTextPos, i - 1)); lastTextPos = i - 1; } stringBuilder.append("++"); lastTextPos += 2; list.add(new SpanDescription(prevId, i - 1, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (EMOJI_SET.contains(current)) { if (lastTextPos < i) { stringBuilder.append(s.substring(lastTextPos, i)); lastTextPos = i; } stringBuilder.append("+"); lastTextPos += 1; list.add(new SpanDescription(current, i, i + 1)); /*stringBuilder.setSpan(new ImageSpan(res, isAlignBottom ? ImageSpan.ALIGN_BOTTOM : ImageSpan.ALIGN_BASELINE), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);*/ prev = 0; prevLong = 0; prevLongCount = 0; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } if (lastTextPos < s.length()) { stringBuilder.append(s.substring(lastTextPos, s.length())); } Spannable spannable = spannableFactory.newSpannable(stringBuilder.toString()); for (SpanDescription description : list) { spannable.setSpan( new EmojiSpan( this, indexes.get(description.id), emojiSideSize, originalMetrics.get(mode)), description.start, description.end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } return spannable; } public Spannable processEmojiMutable(CharSequence s, int mode) { long prev = 0; long prevLong = 0; int prevLongCount = 0; ArrayList<SpanDescription> list = new ArrayList<SpanDescription>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (Arrays.binarySearch(EMOJI_SORTED, prevId) >= 0) { list.add(new SpanDescription(prevId, i - 3, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (Arrays.binarySearch(EMOJI_SORTED, prevId) >= 0) { list.add(new SpanDescription(prevId, i - 1, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (Arrays.binarySearch(EMOJI_SORTED, current) >= 0) { list.add(new SpanDescription(current, i, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } Spannable spannable = spannableFactory.newSpannable(s); for (SpanDescription description : list) { spannable.setSpan( new EmojiSpan( this, indexes.get(description.id), emojiSideSize, originalMetrics.get(mode)), description.start, description.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannable; } public Spannable processEmojiCompatMutable(CharSequence s, int mode) { return processEmojiMutable(s, mode); } public static long[] findFirstUniqEmoji(String s, int count) { long prev = 0; long prevLong = 0; int prevLongCount = 0; long[] res = new long[count]; int index = 0; HashSet<Long> founded = new HashSet<Long>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (EMOJI_SET.contains(prevId) && !founded.contains(prevId)) { founded.add(prevId); res[index++] = prevId; if (index >= count) break; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (EMOJI_SET.contains(prevId) && !founded.contains(prevId)) { founded.add(prevId); res[index++] = prevId; if (index >= count) break; } } if (EMOJI_SET.contains(current) && !founded.contains(current)) { founded.add(current); res[index++] = current; if (index >= count) break; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } if (index == count) { return res; } else { long[] res2 = new long[index]; for (int i = 0; i < index; i++) { res2[i] = res[i]; } return res2; } } public static boolean containsEmoji(CharSequence s) { long prev = 0; long prevLong = 0; int prevLongCount = 0; for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); // if (prevLongCount == 3) { // long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; // if (Arrays.binarySearch(EMOJI_SORTED, prevId) > 0) { // return true; // } // } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if ((current >= minEmoji2) && (current <= maxEmoji2) && Arrays.binarySearch(EMOJI_SORTED, prevId) > 0) { return true; } } if ((current >= minEmoji1) && (current <= maxEmoji1) && Arrays.binarySearch(EMOJI_SORTED, current) > 0) { return true; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } return false; } public int getRectSize() { return rectSize; } public int getSectionIndex(long emoji) { int globalIndex = indexes.get(emoji); int x = globalIndex / COUNT_IN_ROW; int y = globalIndex % COUNT_IN_ROW; return (y / SECTION_SIDE) + (x / SECTION_SIDE) * SECTION_ROW_COUNT; } public int getSectionX(long emoji) { int globalIndex = indexes.get(emoji); return (globalIndex % COUNT_IN_ROW) % SECTION_SIDE; } public int getSectionY(long emoji) { int globalIndex = indexes.get(emoji); return (globalIndex / COUNT_IN_ROW) % SECTION_SIDE; } public Bitmap getSection(int index) { return emojiMap.get(index); } private static Paint bitmapPaint = new Paint(); static { bitmapPaint.setAntiAlias(true); bitmapPaint.setFlags(Paint.FILTER_BITMAP_FLAG); } private static Rect bitmapRect = new Rect(); private static Rect srcRect = new Rect(); private class EmojiSpan extends ReplacementSpan { private SmileProcessor processor; private int offset; private int size; private int padding; private int section; private int sectionX; private int sectionY; private Paint.FontMetricsInt originalMetrics; public EmojiSpan(SmileProcessor processor, int index, int size, Paint.FontMetricsInt original) { this.processor = processor; this.size = size; this.originalMetrics = original; int x = index / COUNT_IN_ROW; int y = index % COUNT_IN_ROW; section = (y / SECTION_SIDE) + (x / SECTION_SIDE) * SECTION_ROW_COUNT; sectionX = y % SECTION_SIDE; sectionY = x % SECTION_SIDE; } @Override public int getSize( Paint paint, CharSequence charSequence, int start, int end, Paint.FontMetricsInt fm) { padding = (int) paint.measureText(" ") / 3; if (fm != null) { fm.ascent = originalMetrics.ascent; fm.descent = originalMetrics.descent; fm.top = originalMetrics.top; fm.bottom = originalMetrics.bottom; } return size + padding * 2; } @Override public void draw( Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { Bitmap srcEmoji = processor.emojiMap.get(section); if (srcEmoji != null) { if (paint.getFontMetrics() != null) { offset = (int) (paint.getFontMetrics().descent); } x += padding; bitmapRect.set((int) x, y - size + offset, (int) (x + size), y + offset); srcRect.set( sectionX * rectSize, sectionY * rectSize, (sectionX + 1) * rectSize, (sectionY + 1) * rectSize); canvas.drawBitmap(srcEmoji, srcRect, bitmapRect, bitmapPaint); } } } }
public Spannable processEmojiCutMutable(String s, int mode) { StringBuilder stringBuilder = new StringBuilder(); long prev = 0; long prevLong = 0; int prevLongCount = 0; int lastTextPos = 0; ArrayList<SpanDescription> list = new ArrayList<SpanDescription>(); for (int i = 0; i < s.length(); i++) { long current = s.charAt(i); if (prevLongCount == 3) { long prevId = ((prevLong & 0xFFFFFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 3) { stringBuilder.append(s.substring(lastTextPos, i - 3)); lastTextPos = i - 3; } stringBuilder.append("++++"); lastTextPos += 4; list.add(new SpanDescription(prevId, i - 3, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (prev != 0) { long prevId = ((prev & 0xFFFF) << 16) + current; if (EMOJI_SET.contains(prevId)) { if (lastTextPos < i - 1) { stringBuilder.append(s.substring(lastTextPos, i - 1)); lastTextPos = i - 1; } stringBuilder.append("++"); lastTextPos += 2; list.add(new SpanDescription(prevId, i - 1, i + 1)); prev = 0; prevLong = 0; prevLongCount = 0; continue; } } if (EMOJI_SET.contains(current)) { if (lastTextPos < i) { stringBuilder.append(s.substring(lastTextPos, i)); lastTextPos = i; } stringBuilder.append("+"); lastTextPos += 1; list.add(new SpanDescription(current, i, i + 1)); /*stringBuilder.setSpan(new ImageSpan(res, isAlignBottom ? ImageSpan.ALIGN_BOTTOM : ImageSpan.ALIGN_BASELINE), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);*/ prev = 0; prevLong = 0; prevLongCount = 0; } else { prev = current; prevLong = ((prevLong & 0xFFFFFFFF) << 16) + current; if (prevLongCount < 3) { prevLongCount++; } } } if (lastTextPos < s.length()) { stringBuilder.append(s.substring(lastTextPos, s.length())); } Spannable spannable = spannableFactory.newSpannable(stringBuilder.toString()); for (SpanDescription description : list) { spannable.setSpan( new EmojiSpan( this, indexes.get(description.id), emojiSideSize, originalMetrics.get(mode)), description.start, description.end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } return spannable; }
/** Sets foreground color for {@code textView}. */ private static void setForegroundColor(TextView textView, int color, int start, int end) { Spannable spannable = Spannable.Factory.getInstance().newSpannable(textView.getText()); spannable.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE); textView.setText(spannable); }