From 93c222fe9c9a18741c0012bad2c4a11958529960 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Sat, 29 Jun 2013 09:51:08 -0700 Subject: [PATCH] Added "recent emoji" panel as an LRU cache. --- res/layout/emoji_drawer.xml | 3 +- .../securesms/components/EmojiDrawer.java | 88 ++++++++++++++----- .../thoughtcrime/securesms/util/Emoji.java | 69 ++++++++++++++- 3 files changed, 131 insertions(+), 29 deletions(-) diff --git a/res/layout/emoji_drawer.xml b/res/layout/emoji_drawer.xml index ee12dbb0ad..900ff47aa3 100644 --- a/res/layout/emoji_drawer.xml +++ b/res/layout/emoji_drawer.xml @@ -11,6 +11,7 @@ android:visibility="visible" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="#ff333333" android:minHeight="100dip"> parent, View view, int position, long id) { - int start = composeText.getSelectionStart(); - int end = composeText.getSelectionEnd (); - String characters = emoji.getEmojiUnicode(position); + String characters; + + if (type == ALL_TYPE ) characters = emoji.getEmojiUnicode(position); + else characters = emoji.getRecentEmojiUnicode(position); + + int start = composeText.getSelectionStart(); + int end = composeText.getSelectionEnd (); composeText.getText().replace(Math.min(start, end), Math.max(start, end), characters, 0, characters.length()); @@ -93,14 +117,27 @@ public class EmojiDrawer extends FrameLayout { TextView.BufferType.SPANNABLE); composeText.setSelection(end+2); + + if (type != RECENT_TYPE) { + emoji.setRecentlyUsed(position); + ((BaseAdapter)recentEmojiGrid.getAdapter()).notifyDataSetChanged(); + } } } - private class AllEmojiGridAdapter extends BaseAdapter { + private class EmojiGridAdapter extends BaseAdapter { + + private final int type; + + public EmojiGridAdapter(int type) { + this.type = type; + } @Override public int getCount() { - return emoji.getEmojiAssetCount(); + if (type == RECENT_TYPE) return emoji.getRecentlyUsedAssetCount(); + else return emoji.getEmojiAssetCount(); + } @Override @@ -115,7 +152,10 @@ public class EmojiDrawer extends FrameLayout { @Override public View getView(int position, View convertView, ViewGroup parent) { - Drawable drawable = emoji.getEmojiDrawable(position); + Drawable drawable; + + if (type == RECENT_TYPE) drawable = emoji.getRecentlyUsed(position); + else drawable = emoji.getEmojiDrawable(position); if (convertView != null && convertView instanceof ImageView) { ((ImageView)convertView).setImageDrawable(drawable); diff --git a/src/org/thoughtcrime/securesms/util/Emoji.java b/src/org/thoughtcrime/securesms/util/Emoji.java index 32fbab2776..0d40dae26d 100644 --- a/src/org/thoughtcrime/securesms/util/Emoji.java +++ b/src/org/thoughtcrime/securesms/util/Emoji.java @@ -1,16 +1,27 @@ package org.thoughtcrime.securesms.util; import android.content.Context; +import android.content.SharedPreferences; import android.graphics.drawable.Drawable; +import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ImageSpan; import android.util.Log; +import com.google.thoughtcrimegson.Gson; +import com.google.thoughtcrimegson.reflect.TypeToken; + import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -48,9 +59,11 @@ public class Emoji { } public String getEmojiUnicode(int position) { - String hexString = emojiAssets[position].split("\\.")[0]; - Integer unicodePoint = Integer.parseInt(hexString, 16); - return new String(Character.toChars(unicodePoint)); + return getEmojiUnicodeFromAssetName(emojiAssets[position]); + } + + public String getRecentEmojiUnicode(int position) { + return getEmojiUnicodeFromAssetName(EmojiLRU.getRecentlyUsed(context).get(position)); } public Drawable getEmojiDrawable(int position) { @@ -74,7 +87,6 @@ public class Emoji { drawable.setBounds(0, 0, (int)(drawable.getIntrinsicWidth()*size), (int)(drawable.getIntrinsicHeight()*size)); - ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); text.setSpan(imageSpan, matches.start(), matches.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -86,6 +98,25 @@ public class Emoji { return text; } + public void setRecentlyUsed(int position) { + String assetName = emojiAssets[position]; + EmojiLRU.putRecentlyUsed(context, assetName); + } + + public int getRecentlyUsedAssetCount() { + return EmojiLRU.getRecentlyUsed(context).size(); + } + + public Drawable getRecentlyUsed(int position) { + return getEmojiDrawable(EmojiLRU.getRecentlyUsed(context).get(position)); + } + + private String getEmojiUnicodeFromAssetName(String assetName) { + String hexString = assetName.split("\\.")[0]; + Integer unicodePoint = Integer.parseInt(hexString, 16); + return new String(Character.toChars(unicodePoint)); + } + private Drawable getEmojiDrawable(String assetName) { try { return Drawable.createFromStream(context.getAssets().open("emoji" + File.separator + assetName), null); @@ -103,4 +134,34 @@ public class Emoji { } } + private static class EmojiLRU { + + private static final String EMOJI_LRU_PREFERENCE = "pref_popular_emoji"; + private static final int EMOJI_LRU_SIZE = 10; + + public static List getRecentlyUsed(Context context) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + String serialized = preferences.getString(EMOJI_LRU_PREFERENCE, "[]"); + Type type = new TypeToken>(){}.getType(); + + return new Gson().fromJson(serialized, type); + } + + public static void putRecentlyUsed(Context context, String asset) { + LinkedHashSet recentlyUsed = new LinkedHashSet(getRecentlyUsed(context)); + recentlyUsed.add(asset); + + if (recentlyUsed.size() > 10) { + Iterator iterator = recentlyUsed.iterator(); + iterator.next(); + iterator.remove(); + } + + String serialized = new Gson().toJson(recentlyUsed); + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(EMOJI_LRU_PREFERENCE, serialized) + .commit(); + } + } }