aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlookshe <github@lookshe.org>2015-08-12 21:43:04 +0200
committerlookshe <github@lookshe.org>2015-08-12 21:43:04 +0200
commit4bb806585f6ccb02a1f25a69dafd57f17f1c7411 (patch)
tree29dcf543b7a7c1869411b838816cc6a2f4b85b84
parent0ee1992da2014d51e42c008b2ae39a6e1e033d0b (diff)
merged latest version from https://github.com/firexel/emojicon/trz/emojicon-merge
but only seems to work with lollipop, so switch back to the old pictures
-rw-r--r--libs/emojicon/build.gradle13
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java83
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiRecentAdapter.java21
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Emojicon.java53
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroup.java39
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroupsLoader.java92
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconPopupDelegate.java196
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java98
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java691
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RecentsEmojiconGroup.java28
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RepeatTouchListener.java74
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/StaticEmojiconGroup.java29
-rw-r--r--libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Updatable.java8
-rw-r--r--libs/emojicon/src/main/res/drawable/ic_keyboard_delete.xml3
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_grid.xml13
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_item.xml51
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_tab.xml6
-rw-r--r--libs/emojicon/src/main/res/layout/emojicon_tab_divider.xml5
-rw-r--r--libs/emojicon/src/main/res/layout/emojicons.xml124
-rw-r--r--libs/emojicon/src/main/res/values/styles.xml81
-rw-r--r--libs/emojicon/src/main/res/xml/emoji.xml7
21 files changed, 1099 insertions, 616 deletions
diff --git a/libs/emojicon/build.gradle b/libs/emojicon/build.gradle
index a9776be0..dca4f703 100644
--- a/libs/emojicon/build.gradle
+++ b/libs/emojicon/build.gradle
@@ -1,12 +1,17 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 19
+ compileSdkVersion 13
buildToolsVersion "21.1.2"
defaultConfig {
- minSdkVersion 8
- targetSdkVersion 19
+ minSdkVersion 14
+ targetSdkVersion 21
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
@@ -18,5 +23,5 @@ android {
}
dependencies {
- compile 'com.android.support:support-v4:19.1.0'
+ compile 'com.android.support:support-v4:21.0.3'
}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
index e3dc221d..b9dd5e59 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java
@@ -16,59 +16,86 @@
package github.ankushsachdeva.emojicon;
-import github.ankushsachdeva.emojicon.EmojiconGridView.OnEmojiconClickedListener;
-import github.ankushsachdeva.emojicon.emoji.Emojicon;
-
+import java.util.ArrayList;
import java.util.List;
import android.content.Context;
+import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
import android.widget.TextView;
-import github.ankushsachdeva.emojicon.R;
-
/**
* @author Ankush Sachdeva (sankush@yahoo.co.in)
*/
-class EmojiAdapter extends ArrayAdapter<Emojicon> {
- OnEmojiconClickedListener emojiClickListener;
- public EmojiAdapter(Context context, List<Emojicon> data) {
- super(context, R.layout.emojicon_item, data);
+class EmojiAdapter extends BaseAdapter {
+ private final List<Emojicon> mEmojicons = new ArrayList<>();
+ private OnEmojiClickedListener mClickListener;
+ private final Context mContext;
+
+ public EmojiAdapter(Context context) {
+ this.mContext = context;
+ }
+
+ public void setEmojiconList(List<Emojicon> emojiconList) {
+ mEmojicons.clear();
+ mEmojicons.addAll(emojiconList);
+ notifyDataSetChanged();
+ }
+
+ public void setClickListener(OnEmojiClickedListener clickListener) {
+ mClickListener = clickListener;
+ }
+
+ @Override
+ public int getCount() {
+ return mEmojicons.size();
}
- public EmojiAdapter(Context context, Emojicon[] data) {
- super(context, R.layout.emojicon_item, data);
+ @Override
+ public Emojicon getItem(int position) {
+ return mEmojicons.get(position);
}
-
- public void setEmojiClickListener(OnEmojiconClickedListener listener){
- this.emojiClickListener = listener;
+
+ @Override
+ public long getItemId(int position) {
+ return position;
}
-
+
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
- v = View.inflate(getContext(), R.layout.emojicon_item, null);
+ v = LayoutInflater.from(mContext).inflate(R.layout.emojicon_item, null);
+ v.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mClickListener != null) {
+ mClickListener.onEmojiClicked(((ViewHolder) v.getTag()).mEmoji);
+ }
+ }
+ });
ViewHolder holder = new ViewHolder();
- holder.icon = (TextView) v.findViewById(R.id.emojicon_icon);
+ holder.mEmojiContainer = (TextView) v.findViewById(R.id.emojiContainer);
+ holder.mEmojiId = (TextView) v.findViewById(R.id.emojiId);
v.setTag(holder);
}
Emojicon emoji = getItem(position);
ViewHolder holder = (ViewHolder) v.getTag();
- holder.icon.setText(emoji.getEmoji());
- holder.icon.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- emojiClickListener.onEmojiconClicked(getItem(position));
- }
- });
+ holder.mEmoji = emoji;
+ holder.mEmojiId.setText(emoji.getId());
+ holder.mEmojiContainer.setText(emoji.toString());
return v;
}
- class ViewHolder {
- TextView icon;
+ private class ViewHolder {
+ TextView mEmojiContainer;
+ TextView mEmojiId;
+ Emojicon mEmoji;
+ }
+
+ public interface OnEmojiClickedListener {
+ void onEmojiClicked(Emojicon emojicon);
}
} \ No newline at end of file
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiRecentAdapter.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiRecentAdapter.java
new file mode 100644
index 00000000..cb4bfb8c
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiRecentAdapter.java
@@ -0,0 +1,21 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+
+/**
+ * Created by aleksandr.naumov on 14.05.2015.
+ */
+class EmojiRecentAdapter extends EmojiAdapter implements Updatable {
+ private final EmojiconRecentsManager mRecentsManager;
+
+ public EmojiRecentAdapter(Context context, EmojiconRecentsManager mRecentsManager) {
+ super(context);
+ this.mRecentsManager = mRecentsManager;
+ update();
+ }
+
+ @Override
+ public void update() {
+ setEmojiconList(mRecentsManager.getEmojiList());
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Emojicon.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Emojicon.java
new file mode 100644
index 00000000..bf8874b2
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Emojicon.java
@@ -0,0 +1,53 @@
+package github.ankushsachdeva.emojicon;
+
+import java.nio.charset.Charset;
+
+/**
+ * Created by aleksandr.naumov on 13.05.2015.
+ */
+public class Emojicon {
+ private final String mStringRepresentation;
+
+ public Emojicon(String stringRepresentation) {
+ byte bytes[] = new byte[stringRepresentation.length() / 2];
+ for (int i = 0; i < stringRepresentation.length(); i += 2) {
+ try {
+ bytes[i / 2] = (byte) Integer.parseInt(stringRepresentation.substring(i, i + 2), 16);
+ } catch (NumberFormatException ex) {
+ throw new RuntimeException(String.format("Cannot parse '%s' as emojicon code", stringRepresentation), ex);
+ }
+ }
+ this.mStringRepresentation = new String(bytes, Charset.forName("Utf-8"));
+ }
+
+ @Override
+ public String toString() {
+ return mStringRepresentation;
+ }
+
+ public String getId() {
+ String id = "";
+ for (byte b : mStringRepresentation.getBytes()) {
+ id += String.format("%x", b);
+ }
+ return id;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Emojicon emojicon = (Emojicon) o;
+ return mStringRepresentation.equals(emojicon.mStringRepresentation);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return mStringRepresentation.hashCode();
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroup.java
new file mode 100644
index 00000000..22ac8f88
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroup.java
@@ -0,0 +1,39 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by aleksandr.naumov on 14.05.2015.
+ */
+abstract class EmojiconGroup {
+ protected final int mIconResId;
+
+ public EmojiconGroup(int iconResId) {
+ mIconResId = iconResId;
+ }
+
+ public static EmojiconGroup fromString(String allEmojicons, int mIconResId) {
+ return new StaticEmojiconGroup(extractEmojicons(allEmojicons), mIconResId);
+ }
+
+ private static List<Emojicon> extractEmojicons(String emojiconsString) {
+ List<Emojicon> emojicons = new ArrayList<>();
+ for (String emojiconString : emojiconsString.split(" ")) {
+ if(!emojiconString.isEmpty()) {
+ emojicons.add(new Emojicon(emojiconString));
+ }
+ }
+ return emojicons;
+ }
+
+ public abstract List<Emojicon> getEmojicons();
+
+ public abstract EmojiAdapter createAdapter(Context context);
+
+ public int getIconResId() {
+ return mIconResId;
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroupsLoader.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroupsLoader.java
new file mode 100644
index 00000000..350e710d
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGroupsLoader.java
@@ -0,0 +1,92 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by aleksandr.naumov on 14.05.2015.
+ */
+class EmojiconGroupsLoader {
+ private static EmojiconGroupsLoader sInstance;
+ private final List<EmojiconGroup> mGroups;
+
+ public static synchronized EmojiconGroupsLoader getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new EmojiconGroupsLoader(context);
+ }
+ return sInstance;
+ }
+
+ private EmojiconGroupsLoader(Context context) {
+ try {
+ mGroups = parseEmojiXml(context);
+ } catch (IOException | XmlPullParserException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private List<EmojiconGroup> parseEmojiXml(Context context) throws IOException, XmlPullParserException {
+ ArrayList<EmojiconGroup> groups = new ArrayList<>();
+ XmlPullParser parser = context.getResources().getXml(R.xml.emoji);
+ parser.next();
+ String groupName = null;
+ for (int eventType = parser.getEventType(); eventType != XmlPullParser.END_DOCUMENT; eventType = parser.next()) {
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ if ("group".equals(parser.getName())) {
+ groupName = parser.getAttributeValue(0);
+ }
+ break;
+ case XmlPullParser.TEXT:
+ if (groupName != null) {
+ groups.add(EmojiconGroup.fromString(
+ parser.getText(),
+ getGroupNameIcon(groupName)
+ ));
+ groupName = null;
+ }
+ break;
+ }
+ }
+ return groups;
+ }
+
+ private int getGroupNameIcon(String groupName) {
+ for (KnownGroupNames knownGroupName : KnownGroupNames.values()) {
+ if (groupName.toLowerCase().equals(knownGroupName.name().toLowerCase())) {
+ return knownGroupName.getIconResId();
+ }
+ }
+ throw new IllegalArgumentException(String.format("Unknown group name '%s'", groupName));
+ }
+
+ public List<EmojiconGroup> getGroups() {
+ return mGroups;
+ }
+
+ public enum KnownGroupNames {
+ RECENT(R.drawable.ic_emoji_recent_light),
+ SMILES(R.drawable.ic_emoji_people_light),
+ NATURE(R.drawable.ic_emoji_nature_light),
+ OBJECTS(R.drawable.ic_emoji_objects_light),
+ TECH(R.drawable.ic_emoji_places_light),
+ SYMBOLS(R.drawable.ic_emoji_symbols_light);
+
+ private final int iconResId;
+
+ KnownGroupNames(int iconResId) {
+ this.iconResId = iconResId;
+ }
+
+ public int getIconResId() {
+ return iconResId;
+ }
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconPopupDelegate.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconPopupDelegate.java
new file mode 100644
index 00000000..48952cdb
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconPopupDelegate.java
@@ -0,0 +1,196 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.PopupWindow;
+
+/**
+ * Created by aleksandr.naumov on 15.05.2015.
+ */
+public class EmojiconPopupDelegate {
+ public static final String KEY_EMOJI_POPUP_SHOWN = "key_emoji_popup_shown";
+ private Context mContext;
+ private EmojiconsPopup mPopup;
+ private EditText mPrefferredEditText;
+ private boolean mPendingShow;
+ private View mRootView;
+
+ @Nullable
+ private PopupShownListener mListener;
+
+ public void setShowHideListener(PopupShownListener listener) {
+ this.mListener = listener;
+ }
+
+ public void setInputEditText(EditText editText) {
+ mPrefferredEditText = editText;
+ }
+
+ public void attach(View rootView) {
+ mContext = createThemedContext(rootView.getContext());
+ mRootView = rootView;
+ mPopup = new EmojiconsPopup(rootView, mContext);
+ mPopup.setOnDismissListener(new DismissListener());
+ mPopup.setOnSoftKeyboardOpenCloseListener(new KeyboardOpenCloseListener());
+ mPopup.setOnEmojiconClickedListener(new EmojiconClickedListener());
+ mPopup.setOnEmojiconBackspaceClickedListener(new BackspaceClickedListener());
+ mPopup.attachGlobalLayoutListener();
+ if (mPendingShow) {
+ show();
+ }
+ }
+
+ private Context createThemedContext(Context context) {
+ TypedArray typedArray = context.obtainStyledAttributes(null, new int[]{R.attr.emojicon_theme});
+ int themeId = typedArray.getResourceId(0, R.style.Emojicon);
+ typedArray.recycle();
+ return new ContextThemeWrapper(context, themeId);
+ }
+
+ public void detach() {
+ hide();
+ if (mPopup != null) {
+ mPopup.detachGlobalLayoutListener();
+ mPopup = null;
+ }
+ mContext = null;
+ mPrefferredEditText = null;
+ mRootView = null;
+ }
+
+ public void saveState(Bundle bundle) {
+ bundle.putBoolean(KEY_EMOJI_POPUP_SHOWN, isShown());
+ }
+
+ public void restoreState(Bundle bundle) {
+ if (bundle.containsKey(KEY_EMOJI_POPUP_SHOWN)) {
+ if (bundle.getBoolean(KEY_EMOJI_POPUP_SHOWN)) {
+ show();
+ } else {
+ hide();
+ }
+ }
+ }
+
+ public void show() {
+ if (mPopup != null && !mPopup.isShowing()) {
+ if (mPopup.isKeyBoardOpen()) {
+ //If keyboard is visible, simply show the emoji popup
+ mPopup.showAtBottom();
+ } else {
+ //else, open the text keyboard first and immediately after that show the emoji popup
+ mPrefferredEditText.setFocusableInTouchMode(true);
+ mPrefferredEditText.requestFocus();
+ mPopup.showAtBottomPending();
+ final InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.showSoftInput(mPrefferredEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ mPendingShow = false;
+ notifyShown();
+ } else if (!mPendingShow) {
+ mPendingShow = true;
+ notifyShown();
+ }
+ }
+
+ public void hide() {
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ notifyHidden();
+ } else if (mPendingShow) {
+ mPendingShow = false;
+ notifyHidden();
+ }
+ }
+
+ private void notifyShown() {
+ if (mListener != null) {
+ mListener.onPopupShown();
+ }
+ }
+
+ private void notifyHidden() {
+ if (mListener != null) {
+ mListener.onPopupHidden();
+ }
+ }
+
+ public boolean isShown() {
+ return mPopup != null ? mPopup.isShowing() : mPendingShow;
+ }
+
+ public void toggle() {
+ if (isShown()) {
+ hide();
+ } else {
+ show();
+ }
+ }
+
+ @Nullable
+ public EditText findFocusedEditText() {
+ View view = mRootView != null ? mRootView.findFocus() : null;
+ if (view instanceof EditText) {
+ return (EditText) view;
+ } else {
+ return null;
+ }
+ }
+
+ public interface PopupShownListener {
+ void onPopupShown();
+
+ void onPopupHidden();
+ }
+
+ private class BackspaceClickedListener implements EmojiconsPopup.OnEmojiconBackspaceClickedListener {
+ @Override
+ public void onEmojiconBackspaceClicked(View v) {
+ EditText currentFocus = findFocusedEditText();
+ if (currentFocus != null) {
+ KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
+ currentFocus.dispatchKeyEvent(event);
+ }
+ }
+ }
+
+ private class EmojiconClickedListener implements EmojiconsPopup.OnEmojiconClickedListener {
+
+ @Override
+ public void onEmojiconClicked(Emojicon emojicon) {
+ EditText currentFocus = findFocusedEditText();
+ if (currentFocus != null) {
+ String textToInsert = emojicon.toString();
+ int start = Math.max(currentFocus.getSelectionStart(), 0);
+ int end = Math.max(currentFocus.getSelectionEnd(), 0);
+ currentFocus.getText().replace(Math.min(start, end), Math.max(start, end), textToInsert, 0, textToInsert.length());
+ }
+ }
+ }
+
+ private class KeyboardOpenCloseListener implements EmojiconsPopup.OnSoftKeyboardOpenCloseListener {
+ @Override
+ public void onKeyboardOpen(int keyBoardHeight) {
+
+ }
+
+ @Override
+ public void onKeyboardClose() {
+ hide();
+ }
+ }
+
+ private class DismissListener implements PopupWindow.OnDismissListener {
+ @Override
+ public void onDismiss() {
+ notifyHidden();
+ }
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
index 9fbb987e..6110d012 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java
@@ -16,43 +16,33 @@
package github.ankushsachdeva.emojicon;
-import github.ankushsachdeva.emojicon.emoji.Emojicon;
-
-import java.util.ArrayList;
-import java.util.StringTokenizer;
+import java.util.Iterator;
+import java.util.LinkedList;
import android.content.Context;
import android.content.SharedPreferences;
/**
-* @author Daniele Ricci
-*/
-public class EmojiconRecentsManager extends ArrayList<Emojicon> {
-
- private static final String PREFERENCE_NAME = "emojicon";
- private static final String PREF_RECENTS = "recent_emojis";
- private static final String PREF_PAGE = "recent_page";
+ * @author Daniele Ricci
+ */
+class EmojiconRecentsManager {
- private static final Object LOCK = new Object();
- private static EmojiconRecentsManager sInstance;
+ private static final String PREFERENCE_NAME = "emojicon_v2";
+ private static final String PREF_RECENTS = "recent_emojis_v2";
+ private static final String PREF_PAGE = "recent_page_v2";
+ private static final String EMOJI_DIVIDER = " ";
+ private final LinkedList<Emojicon> mEmoji;
private Context mContext;
- private EmojiconRecentsManager(Context context) {
+ public EmojiconRecentsManager(Context context) {
mContext = context.getApplicationContext();
- loadRecents();
+ mEmoji = loadRecent();
}
- public static EmojiconRecentsManager getInstance(Context context) {
- if (sInstance == null) {
- synchronized (LOCK) {
- if (sInstance == null) {
- sInstance = new EmojiconRecentsManager(context);
- }
- }
- }
- return sInstance;
+ public LinkedList<Emojicon> getEmojiList() {
+ return mEmoji;
}
public int getRecentPage() {
@@ -64,61 +54,31 @@ public class EmojiconRecentsManager extends ArrayList<Emojicon> {
}
public void push(Emojicon object) {
- // FIXME totally inefficient way of adding the emoji to the adapter
- // TODO this should be probably replaced by a deque
- if (contains(object)) {
- super.remove(object);
+ if (mEmoji.contains(object)) {
+ mEmoji.remove(object);
}
- add(0, object);
- }
-
- @Override
- public boolean add(Emojicon object) {
- boolean ret = super.add(object);
- return ret;
+ mEmoji.addFirst(object);
}
- @Override
- public void add(int index, Emojicon object) {
- super.add(index, object);
- }
-
- @Override
- public boolean remove(Object object) {
- boolean ret = super.remove(object);
- return ret;
- }
-
private SharedPreferences getPreferences() {
return mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
}
- private void loadRecents() {
- SharedPreferences prefs = getPreferences();
- String str = prefs.getString(PREF_RECENTS, "");
- StringTokenizer tokenizer = new StringTokenizer(str, "~");
- while (tokenizer.hasMoreTokens()) {
- try {
- add(new Emojicon(tokenizer.nextToken()));
- }
- catch (NumberFormatException e) {
- // ignored
- }
+ private LinkedList<Emojicon> loadRecent() {
+ String allEmoji = getPreferences().getString(PREF_RECENTS, "");
+ if (allEmoji != null) {
+ return new LinkedList<>(EmojiconGroup.fromString(allEmoji, 0).getEmojicons());
+ } else {
+ return new LinkedList<>();
}
}
-
- public void saveRecents() {
+
+ public void save() {
StringBuilder str = new StringBuilder();
- int c = size();
- for (int i = 0; i < c; i++) {
- Emojicon e = get(i);
- str.append(e.getEmoji());
- if (i < (c - 1)) {
- str.append('~');
- }
+ for (Iterator<Emojicon> iterator = mEmoji.iterator(); iterator.hasNext(); ) {
+ Emojicon emojicon = iterator.next();
+ str.append(emojicon.getId()).append(iterator.hasNext() ? EMOJI_DIVIDER : "");
}
- SharedPreferences prefs = getPreferences();
- prefs.edit().putString(PREF_RECENTS, str.toString()).commit();
+ getPreferences().edit().putString(PREF_RECENTS, str.toString()).apply();
}
-
}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
index 12bc16e0..5bcafccf 100644
--- a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java
@@ -16,32 +16,25 @@
package github.ankushsachdeva.emojicon;
-import github.ankushsachdeva.emojicon.EmojiconGridView.OnEmojiconClickedListener;
-import github.ankushsachdeva.emojicon.emoji.Emojicon;
-import github.ankushsachdeva.emojicon.emoji.Nature;
-import github.ankushsachdeva.emojicon.emoji.Objects;
-import github.ankushsachdeva.emojicon.emoji.People;
-import github.ankushsachdeva.emojicon.emoji.Places;
-import github.ankushsachdeva.emojicon.emoji.Symbols;
-
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.List;
-import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.SystemClock;
+import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
+import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
+import android.widget.GridView;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.PopupWindow;
@@ -49,360 +42,320 @@ import android.widget.PopupWindow;
* @author Ankush Sachdeva (sankush@yahoo.co.in).
*/
-public class EmojiconsPopup extends PopupWindow implements ViewPager.OnPageChangeListener, EmojiconRecents {
- private int mEmojiTabLastSelectedIndex = -1;
- private View[] mEmojiTabs;
- private PagerAdapter mEmojisAdapter;
- private EmojiconRecentsManager mRecentsManager;
- private int keyBoardHeight = 0;
- private Boolean pendingOpen = false;
- private Boolean isOpened = false;
- OnEmojiconClickedListener onEmojiconClickedListener;
- OnEmojiconBackspaceClickedListener onEmojiconBackspaceClickedListener;
- OnSoftKeyboardOpenCloseListener onSoftKeyboardOpenCloseListener;
- View rootView;
- Context mContext;
-
- private ViewPager emojisPager;
- /**
- * Constructor
- * @param rootView The top most layout in your view hierarchy. The difference of this view and the screen height will be used to calculate the keyboard height.
- * @param mContext The context of current activity.
- */
- public EmojiconsPopup(View rootView, Context mContext){
- super(mContext);
- this.mContext = mContext;
- this.rootView = rootView;
- View customView = createCustomView();
- setContentView(customView);
- setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- //default size
- setSize((int) mContext.getResources().getDimension(R.dimen.keyboard_height), LayoutParams.MATCH_PARENT);
- }
- /**
- * Set the listener for the event of keyboard opening or closing.
- */
- public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener){
- this.onSoftKeyboardOpenCloseListener = listener;
- }
-
- /**
- * Set the listener for the event when any of the emojicon is clicked
- */
- public void setOnEmojiconClickedListener(OnEmojiconClickedListener listener){
- this.onEmojiconClickedListener = listener;
- }
-
- /**
- * Set the listener for the event when backspace on emojicon popup is clicked
- */
- public void setOnEmojiconBackspaceClickedListener(OnEmojiconBackspaceClickedListener listener){
- this.onEmojiconBackspaceClickedListener = listener;
- }
-
- /**
- * Use this function to show the emoji popup.
- * NOTE: Since, the soft keyboard sizes are variable on different android devices, the
- * library needs you to open the soft keyboard atleast once before calling this function.
- * If that is not possible see showAtBottomPending() function.
- *
- */
- public void showAtBottom(){
- showAtLocation(rootView, Gravity.BOTTOM, 0, 0);
- }
- /**
- * Use this function when the soft keyboard has not been opened yet. This
- * will show the emoji popup after the keyboard is up next time.
- * Generally, you will be calling InputMethodManager.showSoftInput function after
- * calling this function.
- */
- public void showAtBottomPending(){
- if(isKeyBoardOpen())
- showAtBottom();
- else
- pendingOpen = true;
- }
-
- /**
- *
- * @return Returns true if the soft keyboard is open, false otherwise.
- */
- public Boolean isKeyBoardOpen(){
- return isOpened;
- }
-
- /**
- * Dismiss the popup
- */
- @Override
- public void dismiss() {
- super.dismiss();
- EmojiconRecentsManager
- .getInstance(mContext).saveRecents();
- }
-
- /**
- * Call this function to resize the emoji popup according to your soft keyboard size
- */
- public void setSizeForSoftKeyboard(){
- rootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- Rect r = new Rect();
- rootView.getWindowVisibleDisplayFrame(r);
-
- int screenHeight = rootView.getRootView()
- .getHeight();
- int heightDifference = screenHeight
- - (r.bottom - r.top);
- int resourceId = mContext.getResources()
- .getIdentifier("status_bar_height",
- "dimen", "android");
- if (resourceId > 0) {
- heightDifference -= mContext.getResources()
- .getDimensionPixelSize(resourceId);
- }
- if (heightDifference > 100) {
- keyBoardHeight = heightDifference;
- setSize(LayoutParams.MATCH_PARENT, keyBoardHeight);
- if(isOpened == false){
- if(onSoftKeyboardOpenCloseListener!=null)
- onSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight);
- }
- isOpened = true;
- if(pendingOpen){
- showAtBottom();
- pendingOpen = false;
- }
- }
- else{
- isOpened = false;
- if(onSoftKeyboardOpenCloseListener!=null)
- onSoftKeyboardOpenCloseListener.onKeyboardClose();
- }
- }
- });
- }
-
- /**
- * Manually set the popup window size
- * @param width Width of the popup
- * @param height Height of the popup
- */
- public void setSize(int width, int height){
- setWidth(width);
- setHeight(height);
- }
-
- private View createCustomView() {
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.emojicons, null, false);
- emojisPager = (ViewPager) view.findViewById(R.id.emojis_pager);
- emojisPager.setOnPageChangeListener(this);
- EmojiconRecents recents = this;
- mEmojisAdapter = new EmojisPagerAdapter(
- Arrays.asList(
- new EmojiconRecentsGridView(mContext, null, null, this),
- new EmojiconGridView(mContext, People.DATA, recents, this),
- new EmojiconGridView(mContext, Nature.DATA, recents, this),
- new EmojiconGridView(mContext, Objects.DATA, recents, this),
- new EmojiconGridView(mContext, Places.DATA, recents, this),
- new EmojiconGridView(mContext, Symbols.DATA, recents, this)
- )
- );
- emojisPager.setAdapter(mEmojisAdapter);
- mEmojiTabs = new View[6];
- mEmojiTabs[0] = view.findViewById(R.id.emojis_tab_0_recents);
- mEmojiTabs[1] = view.findViewById(R.id.emojis_tab_1_people);
- mEmojiTabs[2] = view.findViewById(R.id.emojis_tab_2_nature);
- mEmojiTabs[3] = view.findViewById(R.id.emojis_tab_3_objects);
- mEmojiTabs[4] = view.findViewById(R.id.emojis_tab_4_cars);
- mEmojiTabs[5] = view.findViewById(R.id.emojis_tab_5_punctuation);
- for (int i = 0; i < mEmojiTabs.length; i++) {
- final int position = i;
- mEmojiTabs[i].setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- emojisPager.setCurrentItem(position);
- }
- });
- }
- view.findViewById(R.id.emojis_backspace).setOnTouchListener(new RepeatListener(1000, 50, new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if(onEmojiconBackspaceClickedListener != null)
- onEmojiconBackspaceClickedListener.onEmojiconBackspaceClicked(v);
- }
- }));
-
- // get last selected page
- mRecentsManager = EmojiconRecentsManager.getInstance(view.getContext());
- int page = mRecentsManager.getRecentPage();
- // last page was recents, check if there are recents to use
- // if none was found, go to page 1
- if (page == 0 && mRecentsManager.size() == 0) {
- page = 1;
- }
-
- if (page == 0) {
- onPageSelected(page);
- }
- else {
- emojisPager.setCurrentItem(page, false);
- }
- return view;
- }
-
- @Override
- public void addRecentEmoji(Context context, Emojicon emojicon) {
- EmojiconRecentsGridView fragment = ((EmojisPagerAdapter)emojisPager.getAdapter()).getRecentFragment();
- fragment.addRecentEmoji(context, emojicon);
- }
-
-
- @Override
- public void onPageScrolled(int i, float v, int i2) {
- }
-
- @Override
- public void onPageSelected(int i) {
- if (mEmojiTabLastSelectedIndex == i) {
- return;
- }
- switch (i) {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- if (mEmojiTabLastSelectedIndex >= 0 && mEmojiTabLastSelectedIndex < mEmojiTabs.length) {
- mEmojiTabs[mEmojiTabLastSelectedIndex].setSelected(false);
- }
- mEmojiTabs[i].setSelected(true);
- mEmojiTabLastSelectedIndex = i;
- mRecentsManager.setRecentPage(i);
- break;
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int i) {
- }
-
- private static class EmojisPagerAdapter extends PagerAdapter {
- private List<EmojiconGridView> views;
- public EmojiconRecentsGridView getRecentFragment(){
- for (EmojiconGridView it : views) {
- if(it instanceof EmojiconRecentsGridView)
- return (EmojiconRecentsGridView)it;
- }
- return null;
- }
- public EmojisPagerAdapter(List<EmojiconGridView> views) {
- super();
- this.views = views;
- }
-
- @Override
- public int getCount() {
- return views.size();
- }
-
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- View v = views.get(position).rootView;
- ((ViewPager)container).addView(v, 0);
- return v;
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object view) {
- ((ViewPager)container).removeView((View)view);
- }
-
- @Override
- public boolean isViewFromObject(View view, Object key) {
- return key == view;
- }
- }
-
- /**
- * A class, that can be used as a TouchListener on any view (e.g. a Button).
- * It cyclically runs a clickListener, emulating keyboard-like behaviour. First
- * click is fired immediately, next before initialInterval, and subsequent before
- * normalInterval.
- * <p/>
- * <p>Interval is scheduled before the onClick completes, so it has to run fast.
- * If it runs slow, it does not generate skipped onClicks.
- */
- public static class RepeatListener implements View.OnTouchListener {
-
- private Handler handler = new Handler();
-
- private int initialInterval;
- private final int normalInterval;
- private final View.OnClickListener clickListener;
-
- private Runnable handlerRunnable = new Runnable() {
- @Override
- public void run() {
- if (downView == null) {
- return;
- }
- handler.removeCallbacksAndMessages(downView);
- handler.postAtTime(this, downView, SystemClock.uptimeMillis() + normalInterval);
- clickListener.onClick(downView);
- }
- };
-
- private View downView;
-
- /**
- * @param initialInterval The interval before first click event
- * @param normalInterval The interval before second and subsequent click
- * events
- * @param clickListener The OnClickListener, that will be called
- * periodically
- */
- public RepeatListener(int initialInterval, int normalInterval, View.OnClickListener clickListener) {
- if (clickListener == null)
- throw new IllegalArgumentException("null runnable");
- if (initialInterval < 0 || normalInterval < 0)
- throw new IllegalArgumentException("negative interval");
-
- this.initialInterval = initialInterval;
- this.normalInterval = normalInterval;
- this.clickListener = clickListener;
- }
-
- public boolean onTouch(View view, MotionEvent motionEvent) {
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN:
- downView = view;
- handler.removeCallbacks(handlerRunnable);
- handler.postAtTime(handlerRunnable, downView, SystemClock.uptimeMillis() + initialInterval);
- clickListener.onClick(view);
- return true;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_OUTSIDE:
- handler.removeCallbacksAndMessages(downView);
- downView = null;
- return true;
- }
- return false;
- }
- }
-
- public interface OnEmojiconBackspaceClickedListener {
- void onEmojiconBackspaceClicked(View v);
- }
-
- public interface OnSoftKeyboardOpenCloseListener{
- void onKeyboardOpen(int keyBoardHeight);
- void onKeyboardClose();
- }
+public class EmojiconsPopup extends PopupWindow implements ViewPager.OnPageChangeListener, EmojiAdapter.OnEmojiClickedListener {
+ private View[] mEmojiTabs;
+ private EmojiconRecentsManager mRecentsManager;
+ private int keyBoardHeight = 0;
+ private boolean mWaitingForKbOpen = false;
+ private boolean mIsOpened = false;
+ private View mRootView;
+ private Context mContext;
+ private ViewPager mEmojisPager;
+
+ @Nullable
+ private OnSoftKeyboardOpenCloseListener mSoftKeyboardOpenCloseListener;
+
+ @Nullable
+ private OnEmojiconClickedListener mEmojiconClickedListener;
+
+ @Nullable
+ private OnEmojiconBackspaceClickedListener mEmojiconBackspaceClickedListener;
+
+ private GlobalLayoutListener mGlobalLayoutListener = new GlobalLayoutListener();
+
+ /**
+ * Constructor
+ *
+ * @param rootView The top most layout in your view hierarchy. The difference of this view and the screen height will be used to calculate the keyboard height.
+ * @param context The context of current activity.
+ */
+ public EmojiconsPopup(View rootView, Context context) {
+ super(context, null, R.attr.emojicon_dialog_style);
+ mContext = context;
+ mRootView = rootView;
+ mRecentsManager = new EmojiconRecentsManager(context);
+ setContentView(createCustomView());
+ setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ }
+
+ /**
+ * Set the listener for the event of keyboard opening or closing.
+ */
+ public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener) {
+ this.mSoftKeyboardOpenCloseListener = listener;
+ }
+
+ /**
+ * Set the listener for the event when any of the emojicon is clicked
+ */
+ public void setOnEmojiconClickedListener(OnEmojiconClickedListener listener) {
+ this.mEmojiconClickedListener = listener;
+ }
+
+ /**
+ * Set the listener for the event when backspace on emojicon popup is clicked
+ */
+ public void setOnEmojiconBackspaceClickedListener(OnEmojiconBackspaceClickedListener listener) {
+ this.mEmojiconBackspaceClickedListener = listener;
+ }
+
+ /**
+ * Use this function to show the emoji popup.
+ * NOTE: Since, the soft keyboard sizes are variable on different android devices, the
+ * library needs you to open the soft keyboard atleast once before calling this function.
+ * If that is not possible see showAtBottomPending() function.
+ */
+ public void showAtBottom() {
+ showAtLocation(mRootView, Gravity.BOTTOM, 0, 0);
+ }
+
+ /**
+ * Use this function when the soft keyboard has not been opened yet. This
+ * will show the emoji popup after the keyboard is up next time.
+ * Generally, you will be calling InputMethodManager.showSoftInput function after
+ * calling this function.
+ */
+ public void showAtBottomPending() {
+ if (isKeyBoardOpen()) {
+ showAtBottom();
+ } else {
+ mWaitingForKbOpen = true;
+ }
+ }
+
+ /**
+ * @return Returns true if the soft keyboard is open, false otherwise.
+ */
+ public Boolean isKeyBoardOpen() {
+ return mIsOpened;
+ }
+
+ /**
+ * Dismiss the popup
+ */
+ @Override
+ public void dismiss() {
+ super.dismiss();
+ mRecentsManager.save();
+ }
+
+ /**
+ * Call this function to resize the emoji popup according to your soft keyboard size
+ */
+ public void attachGlobalLayoutListener() {
+ mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
+ }
+
+ public void detachGlobalLayoutListener() {
+ mRootView.getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+
+ /**
+ * Manually set the popup window size
+ *
+ * @param width Width of the popup
+ * @param height Height of the popup
+ */
+ public void setSize(int width, int height) {
+ setWidth(width);
+ setHeight(height);
+ }
+
+ private View createCustomView() {
+ View view = LayoutInflater.from(mContext).inflate(R.layout.emojicons, null);
+ List<EmojiconGroup> displayedGroups = collectDisplayedGroups();
+ initViewPager(view, displayedGroups);
+ initTabs(view, displayedGroups);
+ initCurrentPage();
+ return view;
+ }
+
+ private void initCurrentPage() {
+ // get last selected page
+ int page = mRecentsManager.getRecentPage();
+ // last page was recents, check if there are recents to use
+ // if none was found, go to page 1
+ if (page == 0 && mRecentsManager.getEmojiList().isEmpty()) {
+ page = 1;
+ }
+ if (page == 0) {
+ onPageSelected(page);
+ } else {
+ mEmojisPager.setCurrentItem(page, false);
+ }
+ }
+
+ private void initTabs(View view, List<EmojiconGroup> displayedGroups) {
+ LinearLayout tabHostLayout = (LinearLayout) view.findViewById(R.id.emojis_tab);
+ mEmojiTabs = new View[displayedGroups.size()];
+ for (int i = 0; i < displayedGroups.size(); i++) {
+ final int position = i;
+ mEmojiTabs[i] = inflateTab(tabHostLayout, displayedGroups.get(i).getIconResId());
+ mEmojiTabs[i].setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mEmojisPager.setCurrentItem(position);
+ }
+ });
+ inflateDivider(tabHostLayout);
+ }
+ View backSpace = inflateTab(tabHostLayout, R.drawable.ic_keyboard_delete);
+ backSpace.setOnTouchListener(new RepeatTouchListener(1000, 50, new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mEmojiconBackspaceClickedListener != null) {
+ mEmojiconBackspaceClickedListener.onEmojiconBackspaceClicked(v);
+ }
+ }
+ }));
+ }
+
+ private View inflateTab(LinearLayout tabHostLayout, int iconResId) {
+ ImageButton tabView = (ImageButton) LayoutInflater.from(mContext)
+ .inflate(R.layout.emojicon_tab, tabHostLayout, false);
+
+ tabHostLayout.addView(tabView);
+ tabView.setImageDrawable(mContext.getResources().getDrawable(iconResId));
+ return tabView;
+ }
+
+ private View inflateDivider(LinearLayout tabHostLayout) {
+ return LayoutInflater.from(mContext).inflate(R.layout.emojicon_tab_divider, tabHostLayout);
+ }
+
+ private void initViewPager(View view, List<EmojiconGroup> displayedGroups) {
+ mEmojisPager = (ViewPager) view.findViewById(R.id.emojis_pager);
+ mEmojisPager.setOnPageChangeListener(this);
+ mEmojisPager.setAdapter(new EmojisPagerAdapter(displayedGroups));
+ }
+
+ private List<EmojiconGroup> collectDisplayedGroups() {
+ List<EmojiconGroup> groups = new ArrayList<>();
+ groups.add(new RecentsEmojiconGroup(mRecentsManager, EmojiconGroupsLoader.KnownGroupNames.RECENT.getIconResId()));
+ groups.addAll(EmojiconGroupsLoader.getInstance(mContext).getGroups());
+ return groups;
+ }
+
+ @Override
+ public void onPageScrolled(int i, float v, int i2) {
+ // ignore
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ for (int i = 0; i < mEmojiTabs.length; i++) {
+ mEmojiTabs[i].setSelected(i == position);
+ }
+ mRecentsManager.setRecentPage(position);
+ EmojiAdapter adapter = ((EmojisPagerAdapter) mEmojisPager.getAdapter()).getPageAdapter(position);
+ if (adapter instanceof Updatable) {
+ ((Updatable) adapter).update();
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int i) {
+ // ignore
+ }
+
+ @Override
+ public void onEmojiClicked(Emojicon emojicon) {
+ mRecentsManager.push(emojicon);
+ if (mEmojiconClickedListener != null) {
+ mEmojiconClickedListener.onEmojiconClicked(emojicon);
+ }
+ }
+
+ private class EmojisPagerAdapter extends PagerAdapter {
+ private final List<EmojiconGroup> mGroups;
+ private final SparseArray<EmojiAdapter> mAdapters = new SparseArray<>();
+
+ public EmojisPagerAdapter(List<EmojiconGroup> groups) {
+ mGroups = groups;
+ }
+
+ public EmojiAdapter getPageAdapter(int pageIndex) {
+ return mAdapters.get(pageIndex);
+ }
+
+ @Override
+ public int getCount() {
+ return mGroups.size();
+ }
+
+ @Override
+ public GridView instantiateItem(ViewGroup container, final int viewPosition) {
+ GridView gridView = (GridView) LayoutInflater.from(mContext).inflate(R.layout.emojicon_grid, container, false);
+ container.addView(gridView);
+ final EmojiAdapter adapter = mGroups.get(viewPosition).createAdapter(mContext);
+ gridView.setAdapter(adapter);
+ adapter.setClickListener(EmojiconsPopup.this);
+ mAdapters.put(viewPosition, adapter);
+ return gridView;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object view) {
+ container.removeView((View) view);
+ mAdapters.remove(position);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object key) {
+ return key == view;
+ }
+ }
+
+ public interface OnEmojiconClickedListener {
+ void onEmojiconClicked(Emojicon emojicon);
+ }
+
+ public interface OnEmojiconBackspaceClickedListener {
+ void onEmojiconBackspaceClicked(View v);
+ }
+
+ public interface OnSoftKeyboardOpenCloseListener {
+ void onKeyboardOpen(int keyBoardHeight);
+
+ void onKeyboardClose();
+ }
+
+ private class GlobalLayoutListener implements OnGlobalLayoutListener {
+ @Override
+ public void onGlobalLayout() {
+ Rect r = new Rect();
+ mRootView.getWindowVisibleDisplayFrame(r);
+
+ int screenHeight = mRootView.getRootView().getHeight();
+ int heightDifference = screenHeight - (r.bottom - r.top);
+ int resourceId = mContext.getResources()
+ .getIdentifier("status_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ heightDifference -= mContext.getResources().getDimensionPixelSize(resourceId);
+ }
+ if (heightDifference > 100) {
+ int oldHeight = getHeight();
+ keyBoardHeight = heightDifference;
+ setSize(LayoutParams.MATCH_PARENT, keyBoardHeight);
+ if (!mIsOpened) {
+ if (mSoftKeyboardOpenCloseListener != null) {
+ mSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight);
+ }
+ }
+ mIsOpened = true;
+ if (mWaitingForKbOpen) {
+ showAtBottom();
+ mWaitingForKbOpen = false;
+ } else if(isShowing() && oldHeight != keyBoardHeight) {
+ dismiss();
+ showAtBottom();
+ }
+ } else {
+ mIsOpened = false;
+ if (mSoftKeyboardOpenCloseListener != null) {
+ mSoftKeyboardOpenCloseListener.onKeyboardClose();
+ }
+ }
+ }
+ }
}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RecentsEmojiconGroup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RecentsEmojiconGroup.java
new file mode 100644
index 00000000..815aca2d
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RecentsEmojiconGroup.java
@@ -0,0 +1,28 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+
+import java.util.List;
+
+/**
+ * Created by aleksandr.naumov on 14.05.2015.
+ */
+class RecentsEmojiconGroup extends EmojiconGroup {
+
+ private final EmojiconRecentsManager mRecentsManager;
+
+ public RecentsEmojiconGroup(EmojiconRecentsManager mRecentsManager, int iconResId) {
+ super(iconResId);
+ this.mRecentsManager = mRecentsManager;
+ }
+
+ @Override
+ public List<Emojicon> getEmojicons() {
+ return mRecentsManager.getEmojiList();
+ }
+
+ @Override
+ public EmojiAdapter createAdapter(Context context) {
+ return new EmojiRecentAdapter(context, mRecentsManager);
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RepeatTouchListener.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RepeatTouchListener.java
new file mode 100644
index 00000000..e8f8e0fb
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/RepeatTouchListener.java
@@ -0,0 +1,74 @@
+package github.ankushsachdeva.emojicon;
+
+import android.os.Handler;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * A class, that can be used as a TouchListener on any view (e.g. a Button).
+ * It cyclically runs a clickListener, emulating keyboard-like behaviour. First
+ * click is fired immediately, next before initialInterval, and subsequent before
+ * normalInterval.
+ * <p/>
+ * <p>Interval is scheduled before the onClick completes, so it has to run fast.
+ * If it runs slow, it does not generate skipped onClicks.
+ */
+class RepeatTouchListener implements View.OnTouchListener {
+
+ private Handler handler = new Handler();
+
+ private int initialInterval;
+ private final int normalInterval;
+ private final View.OnClickListener clickListener;
+
+ private Runnable handlerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (downView == null) {
+ return;
+ }
+ handler.removeCallbacksAndMessages(downView);
+ handler.postAtTime(this, downView, SystemClock.uptimeMillis() + normalInterval);
+ clickListener.onClick(downView);
+ }
+ };
+
+ private View downView;
+
+ /**
+ * @param initialInterval The interval before first click event
+ * @param normalInterval The interval before second and subsequent click
+ * events
+ * @param clickListener The OnClickListener, that will be called
+ * periodically
+ */
+ public RepeatTouchListener(int initialInterval, int normalInterval, View.OnClickListener clickListener) {
+ if (clickListener == null)
+ throw new IllegalArgumentException("null runnable");
+ if (initialInterval < 0 || normalInterval < 0)
+ throw new IllegalArgumentException("negative interval");
+
+ this.initialInterval = initialInterval;
+ this.normalInterval = normalInterval;
+ this.clickListener = clickListener;
+ }
+
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ downView = view;
+ handler.removeCallbacks(handlerRunnable);
+ handler.postAtTime(handlerRunnable, downView, SystemClock.uptimeMillis() + initialInterval);
+ clickListener.onClick(view);
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_OUTSIDE:
+ handler.removeCallbacksAndMessages(downView);
+ downView = null;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/StaticEmojiconGroup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/StaticEmojiconGroup.java
new file mode 100644
index 00000000..ae9a767d
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/StaticEmojiconGroup.java
@@ -0,0 +1,29 @@
+package github.ankushsachdeva.emojicon;
+
+import android.content.Context;
+
+import java.util.List;
+
+/**
+ * Created by aleksandr.naumov on 13.05.2015.
+ */
+class StaticEmojiconGroup extends EmojiconGroup {
+ private final List<Emojicon> mEmojicons;
+
+ public StaticEmojiconGroup(List<Emojicon> emojicons, int iconResId) {
+ super(iconResId);
+ mEmojicons = emojicons;
+ }
+
+ @Override
+ public List<Emojicon> getEmojicons() {
+ return mEmojicons;
+ }
+
+ @Override
+ public EmojiAdapter createAdapter(Context context) {
+ EmojiAdapter adapter = new EmojiAdapter(context);
+ adapter.setEmojiconList(getEmojicons());
+ return adapter;
+ }
+}
diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Updatable.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Updatable.java
new file mode 100644
index 00000000..04ce9f11
--- /dev/null
+++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/Updatable.java
@@ -0,0 +1,8 @@
+package github.ankushsachdeva.emojicon;
+
+/**
+ * Created by aleksandr.naumov on 14.05.2015.
+ */
+interface Updatable {
+ void update();
+}
diff --git a/libs/emojicon/src/main/res/drawable/ic_keyboard_delete.xml b/libs/emojicon/src/main/res/drawable/ic_keyboard_delete.xml
new file mode 100644
index 00000000..0e627f5c
--- /dev/null
+++ b/libs/emojicon/src/main/res/drawable/ic_keyboard_delete.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/sym_keyboard_delete_holo_dark"/> \ No newline at end of file
diff --git a/libs/emojicon/src/main/res/layout/emojicon_grid.xml b/libs/emojicon/src/main/res/layout/emojicon_grid.xml
index 457f8756..d82f07a3 100644
--- a/libs/emojicon/src/main/res/layout/emojicon_grid.xml
+++ b/libs/emojicon/src/main/res/layout/emojicon_grid.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2013 Klamr. All rights reserved.
~
~ This software is the confidential and proprietary information of Klamr or one of its
@@ -16,12 +15,6 @@
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/Emoji_GridView"
+ style="?emojicon_grid_style"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/transparent"
- android:cacheColorHint="@android:color/transparent"
- android:columnWidth="40dip"
- android:horizontalSpacing="0dip"
- android:numColumns="auto_fit"
- android:scrollbars="vertical"
- android:verticalSpacing="0dip" />
+ android:layout_height="match_parent" />
diff --git a/libs/emojicon/src/main/res/layout/emojicon_item.xml b/libs/emojicon/src/main/res/layout/emojicon_item.xml
index ed11041a..c5e765cb 100644
--- a/libs/emojicon/src/main/res/layout/emojicon_item.xml
+++ b/libs/emojicon/src/main/res/layout/emojicon_item.xml
@@ -1,32 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright 2014 Ankush Sachdeva
- ~
- ~ 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.
- -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:emojicon="http://schemas.android.com/apk/res-auto"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ style="?emojicon_grid_item_style"
+ android:layout_width="64dp"
+ android:layout_height="64dp">
+
+ <TextView
+ android:id="@+id/emojiId"
+ style="?emojicon_id_style"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="A68" />
+
+ <TextView
+ android:id="@+id/emojiContainer"
+ style="?emojicon_item_style"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <github.ankushsachdeva.emojicon.EmojiconTextView
- android:layout_gravity="center"
- android:id="@+id/emojicon_icon"
- android:layout_width="36dip"
- android:layout_height="36dip"
- emojicon:emojiconSize="30dip"
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:gravity="center"/>
-</FrameLayout> \ No newline at end of file
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_gravity="center"
+ tools:text=";)" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/libs/emojicon/src/main/res/layout/emojicon_tab.xml b/libs/emojicon/src/main/res/layout/emojicon_tab.xml
new file mode 100644
index 00000000..51492c38
--- /dev/null
+++ b/libs/emojicon/src/main/res/layout/emojicon_tab.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ style="?emojicon_tab_item_style"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1" /> \ No newline at end of file
diff --git a/libs/emojicon/src/main/res/layout/emojicon_tab_divider.xml b/libs/emojicon/src/main/res/layout/emojicon_tab_divider.xml
new file mode 100644
index 00000000..0228cc9e
--- /dev/null
+++ b/libs/emojicon/src/main/res/layout/emojicon_tab_divider.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ style="?emojicon_divider_style"
+ android:layout_width="1px"
+ android:layout_height="match_parent" /> \ No newline at end of file
diff --git a/libs/emojicon/src/main/res/layout/emojicons.xml b/libs/emojicon/src/main/res/layout/emojicons.xml
index 287923d1..f1e71624 100644
--- a/libs/emojicon/src/main/res/layout/emojicons.xml
+++ b/libs/emojicon/src/main/res/layout/emojicons.xml
@@ -1,119 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright 2014 Ankush Sachdeva
- ~
- ~ 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.
- -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="@drawable/keyboard_background_holo"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ style="?emojicon_kbd_style"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
<LinearLayout
- android:id="@+id/emojis_tab"
- android:layout_width="match_parent"
- android:layout_height="50dip"
- android:layout_alignParentTop="true"
- android:orientation="horizontal">
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_0_recents"
- android:src="@drawable/ic_emoji_recent_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_1_people"
- android:src="@drawable/ic_emoji_people_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_2_nature"
- android:src="@drawable/ic_emoji_nature_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_3_objects"
- android:src="@drawable/ic_emoji_objects_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_4_cars"
- android:src="@drawable/ic_emoji_places_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:scaleType="center"
- android:id="@+id/emojis_tab_5_punctuation"
- android:src="@drawable/ic_emoji_symbols_light"/>
- <View
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:background="#888888"/>
- <ImageButton
- android:background="@null"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:id="@+id/emojis_backspace"
- android:src="@drawable/sym_keyboard_delete_holo_dark"/>
- </LinearLayout>
+ android:id="@+id/emojis_tab"
+ style="?emojicon_tab_bar_style"
+ android:layout_width="match_parent"
+ android:layout_height="?emojicon_tab_bar_height"
+ android:layout_alignParentTop="true"
+ android:orientation="horizontal" />
+
<android.support.v4.view.ViewPager
- android:layout_below="@id/emojis_tab"
android:id="@+id/emojis_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"/>
+ android:layout_alignParentBottom="true"
+ android:layout_below="@id/emojis_tab" />
+
<View
+ style="?emojicon_divider_style"
android:layout_width="match_parent"
android:layout_height="1px"
- android:layout_below="@id/emojis_tab"
- android:background="#8f8f8f"/>
+ android:layout_below="@id/emojis_tab" />
+
</RelativeLayout>
diff --git a/libs/emojicon/src/main/res/values/styles.xml b/libs/emojicon/src/main/res/values/styles.xml
new file mode 100644
index 00000000..8cd79f02
--- /dev/null
+++ b/libs/emojicon/src/main/res/values/styles.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <attr name="emojicon_theme" format="reference" />
+
+ <attr name="emojicon_item_style" format="reference" />
+ <attr name="emojicon_id_style" format="reference" />
+ <attr name="emojicon_dialog_style" format="reference" />
+ <attr name="emojicon_divider_style" format="reference" />
+ <attr name="emojicon_grid_style" format="reference" />
+ <attr name="emojicon_grid_item_style" format="reference" />
+ <attr name="emojicon_tab_bar_style" format="reference" />
+ <attr name="emojicon_tab_bar_height" format="dimension|reference" />
+ <attr name="emojicon_tab_item_style" format="reference" />
+ <attr name="emojicon_kbd_style" format="reference" />
+
+ <style name="Emojicon">
+ <item name="emojicon_item_style">@style/Emojicon.Item</item>
+ <item name="emojicon_id_style">@style/Emojicon.Id</item>
+ <item name="emojicon_dialog_style">@style/Emojicon.Dialog</item>
+ <item name="emojicon_divider_style">@style/Emojicon.Divider</item>
+ <item name="emojicon_grid_style">@style/Emojicon.Grid</item>
+ <item name="emojicon_grid_item_style">@style/Emojicon.GridItem</item>
+ <item name="emojicon_tab_bar_style">@style/Emojicon.TabBar</item>
+ <item name="emojicon_tab_bar_height">50dp</item>
+ <item name="emojicon_tab_item_style">@style/Emojicon.TabItem</item>
+ <item name="emojicon_kbd_style">@style/Emojicon.Kbd</item>
+ </style>
+
+ <style name="Emojicon.Item">
+ <item name="android:gravity">center</item>
+ <item name="android:textSize">30sp</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+
+ <style name="Emojicon.Id">
+ <item name="android:textSize">8sp</item>
+ <item name="android:textColor">#80ffffff</item>
+ </style>
+
+ <style name="Emojicon.Dialog" parent="@android:style/Theme.Dialog">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:popupAnimationStyle">@null</item>
+ <item name="android:popupBackground">@null</item>
+ </style>
+
+ <style name="Emojicon.Divider">
+ <item name="android:background">#888888</item>
+ </style>
+
+ <style name="Emojicon.Grid">
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:cacheColorHint">@android:color/transparent</item>
+ <item name="android:columnWidth">68dp</item>
+ <item name="android:horizontalSpacing">0dp</item>
+ <item name="android:numColumns">auto_fit</item>
+ <item name="android:scrollbars">vertical</item>
+ <item name="android:verticalSpacing">0dp</item>
+ </style>
+
+ <style name="Emojicon.GridItem">
+ <item name="android:minHeight">64dp</item>
+ </style>
+
+ <style name="Emojicon.TabBar">
+ <item name="android:background">@null</item>
+ </style>
+
+ <style name="Emojicon.TabItem">
+ <item name="android:background">@null</item>
+ </style>
+
+ <style name="Emojicon.Kbd">
+ <item name="android:background">@drawable/keyboard_background_holo</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/libs/emojicon/src/main/res/xml/emoji.xml b/libs/emojicon/src/main/res/xml/emoji.xml
new file mode 100644
index 00000000..90979898
--- /dev/null
+++ b/libs/emojicon/src/main/res/xml/emoji.xml
@@ -0,0 +1,7 @@
+<emoji>
+ <group name="smiles">F09F9883 F09F9880 F09F988A E298BA F09F9889 F09F988D F09F9898 F09F989A F09F9897 F09F9899 F09F989C F09F989D F09F989B F09F98B3 F09F9881 F09F9894 F09F988C F09F9892 F09F989E F09F98A3 F09F98A2 F09F9882 F09F98AD F09F98AA F09F98A5 F09F98B0 F09F9885 F09F9893 F09F98A9 F09F98AB F09F98A8 F09F98B1 F09F98A0 F09F98A1 F09F98A4 F09F9896 F09F9886 F09F988B F09F98B7 F09F988E F09F98B4 F09F98B5 F09F98B2 F09F989F F09F98A6 F09F98A7 F09F9888 F09F91BF F09F98AE F09F98AC F09F9890 F09F9895 F09F98AF F09F98B6 F09F9887 F09F988F F09F9891 F09F91B2 F09F91B3 F09F91AE F09F91B7 F09F9282 F09F91B6 F09F91A6 F09F91A7 F09F91A8 F09F91A9 F09F91B4 F09F91B5 F09F91B1 F09F91BC F09F91B8 F09F98BA F09F98B8 F09F98BB F09F98BD F09F98BC F09F9980 F09F98BF F09F98B9 F09F98BE F09F91B9 F09F91BA F09F9988 F09F9989 F09F998A F09F9280 F09F91BD F09F92A9 F09F94A5 E29CA8 F09F8C9F F09F92AB F09F92A5 F09F92A2 F09F92A6 F09F92A7 F09F92A4 F09F92A8 F09F9182 F09F9180 F09F9183 F09F9185 F09F9184 F09F918D F09F918E F09F918C F09F918A E29C8A E29C8C F09F918B E29C8B F09F9190 F09F9186 F09F9187 F09F9189 F09F9188 F09F998C F09F998F E2989D F09F918F F09F92AA F09F9AB6 F09F8F83 F09F9283 F09F91AB F09F91AA F09F91AC F09F91AD F09F928F F09F9291 F09F91AF F09F9986 F09F9985 F09F9281 F09F998B F09F9286 F09F9287 F09F9285 F09F91B0 F09F998E F09F998D F09F9987 F09F8EA9 F09F9191 F09F9192 F09F919F F09F919E F09F91A1 F09F91A0 F09F91A2 F09F9195 F09F9194 F09F919A F09F9197 F09F8EBD F09F9196 F09F9198 F09F9199 F09F92BC F09F919C F09F919D F09F919B F09F9193 F09F8E80 F09F8C82 F09F9284 F09F929B F09F9299 F09F929C F09F929A E29DA4 F09F9294 F09F9297 F09F9293 F09F9295 F09F9296 F09F929E F09F9298 F09F928C F09F928B F09F928D F09F928E F09F91A4 F09F91A5 F09F92AC F09F91A3 F09F92AD</group>
+ <group name="nature">F09F90B6 F09F90BA F09F90B1 F09F90AD F09F90B9 F09F90B0 F09F90B8 F09F90AF F09F90A8 F09F90BB F09F90B7 F09F90BD F09F90AE F09F9097 F09F90B5 F09F9092 F09F90B4 F09F9091 F09F9098 F09F90BC F09F90A7 F09F90A6 F09F90A4 F09F90A5 F09F90A3 F09F9094 F09F908D F09F90A2 F09F909B F09F909D F09F909C F09F909E F09F908C F09F9099 F09F909A F09F90A0 F09F909F F09F90AC F09F90B3 F09F908B F09F9084 F09F908F F09F9080 F09F9083 F09F9085 F09F9087 F09F9089 F09F908E F09F9090 F09F9093 F09F9095 F09F9096 F09F9081 F09F9082 F09F90B2 F09F90A1 F09F908A F09F90AB F09F90AA F09F9086 F09F9088 F09F90A9 F09F90BE F09F9290 F09F8CB8 F09F8CB7 F09F8D80 F09F8CB9 F09F8CBB F09F8CBA F09F8D81 F09F8D83 F09F8D82 F09F8CBF F09F8CBE F09F8D84 F09F8CB5 F09F8CB4 F09F8CB2 F09F8CB3 F09F8CB0 F09F8CB1 F09F8CBC F09F8C90 F09F8C9E F09F8C9D F09F8C9A F09F8C91 F09F8C92 F09F8C93 F09F8C94 F09F8C95 F09F8C96 F09F8C97 F09F8C98 F09F8C9C F09F8C9B F09F8C99 F09F8C8D F09F8C8E F09F8C8F F09F8C8B F09F8C8C F09F8CA0 E2AD90 E29880 E29B85 E29881 E29AA1 E29894 E29D84 E29B84 F09F8C80 F09F8C81 F09F8C88 F09F8C8A</group>
+ <group name="objects">F09F8E8D F09F929D F09F8E8E F09F8E92 F09F8E93 F09F8E8F F09F8E86 F09F8E87 F09F8E90 F09F8E91 F09F8E83 F09F91BB F09F8E85 F09F8E84 F09F8E81 F09F8E8B F09F8E89 F09F8E8A F09F8E88 F09F8E8C F09F94AE F09F8EA5 F09F93B7 F09F93B9 F09F93BC F09F92BF F09F9380 F09F92BD F09F92BE F09F92BB F09F93B1 E2988E F09F939E F09F939F F09F93A0 F09F93A1 F09F93BA F09F93BB F09F948A F09F9489 F09F9488 F09F9487 F09F9494 F09F9495 F09F93A2 F09F93A3 E28FB3 E28C9B E28FB0 E28C9A F09F9493 F09F9492 F09F948F F09F9490 F09F9491 F09F948E F09F92A1 F09F94A6 F09F9486 F09F9485 F09F948C F09F948B F09F948D F09F9B81 F09F9B80 F09F9ABF F09F9ABD F09F94A7 F09F94A9 F09F94A8 F09F9AAA F09F9AAC F09F92A3 F09F94AB F09F94AA F09F928A F09F9289 F09F92B0 F09F92B4 F09F92B5 F09F92B7 F09F92B6 F09F92B3 F09F92B8 F09F93B2 F09F93A7 F09F93A5 F09F93A4 E29C89 F09F93A9 F09F93A8 F09F93AF F09F93AB F09F93AA F09F93AC F09F93AD F09F93AE F09F93A6 F09F939D F09F9384 F09F9383 F09F9391 F09F938A F09F9388 F09F9389 F09F939C F09F938B F09F9385 F09F9386 F09F9387 F09F9381 F09F9382 E29C82 F09F938C F09F938E E29C92 E29C8F F09F938F F09F9390 F09F9395 F09F9397 F09F9398 F09F9399 F09F9393 F09F9394 F09F9392 F09F939A F09F9396 F09F9496 F09F939B F09F94AC F09F94AD F09F93B0 F09F8EA8 F09F8EAC F09F8EA4 F09F8EA7 F09F8EBC F09F8EB5 F09F8EB6 F09F8EB9 F09F8EBB F09F8EBA F09F8EB7 F09F8EB8 F09F91BE F09F8EAE F09F838F F09F8EB4 F09F8084 F09F8EB2 F09F8EAF F09F8F88 F09F8F80 E29ABD E29ABE F09F8EBE F09F8EB1 F09F8F89 F09F8EB3 E29BB3 F09F9AB5 F09F9AB4 F09F8F81 F09F8F87 F09F8F86 F09F8EBF F09F8F82 F09F8F8A F09F8F84 F09F8EA3 E29895 F09F8DB5 F09F8DB6 F09F8DBC F09F8DBA F09F8DBB F09F8DB8 F09F8DB9 F09F8DB7 F09F8DB4 F09F8D95 F09F8D94 F09F8D9F F09F8D97 F09F8D96 F09F8D9D F09F8D9B F09F8DA4 F09F8DB1 F09F8DA3 F09F8DA5 F09F8D99 F09F8D98 F09F8D9A F09F8D9C F09F8DB2 F09F8DA2 F09F8DA1 F09F8DB3 F09F8D9E F09F8DA9 F09F8DAE F09F8DA6 F09F8DA8 F09F8DA7 F09F8E82 F09F8DB0 F09F8DAA F09F8DAB F09F8DAC F09F8DAD F09F8DAF F09F8D8E F09F8D8F F09F8D8A F09F8D8B F09F8D92 F09F8D87 F09F8D89 F09F8D93 F09F8D91 F09F8D88 F09F8D8C F09F8D90 F09F8D8D F09F8DA0 F09F8D86 F09F8D85 F09F8CBD</group>
+ <group name="tech">F09F8FA0 F09F8FA1 F09F8FAB F09F8FA2 F09F8FA3 F09F8FA5 F09F8FA6 F09F8FAA F09F8FA9 F09F8FA8 F09F9292 E29BAA F09F8FAC F09F8FA4 F09F8C87 F09F8C86 F09F8FAF F09F8FB0 E29BBA F09F8FAD F09F97BC F09F97BE F09F97BB F09F8C84 F09F8C85 F09F8C83 F09F97BD F09F8C89 F09F8EA0 F09F8EA1 E29BB2 F09F8EA2 F09F9AA2 E29BB5 F09F9AA4 F09F9AA3 E29A93 F09F9A80 E29C88 F09F92BA F09F9A81 F09F9A82 F09F9A8A F09F9A89 F09F9A9E F09F9A86 F09F9A84 F09F9A85 F09F9A88 F09F9A87 F09F9A9D F09F9A8B F09F9A83 F09F9A8E F09F9A8C F09F9A8D F09F9A99 F09F9A98 F09F9A97 F09F9A95 F09F9A96 F09F9A9B F09F9A9A F09F9AA8 F09F9A93 F09F9A94 F09F9A92 F09F9A91 F09F9A90 F09F9AB2 F09F9AA1 F09F9A9F F09F9AA0 F09F9A9C F09F9288 F09F9A8F F09F8EAB F09F9AA6 F09F9AA5 E29AA0 F09F9AA7 F09F94B0 E29BBD F09F8FAE F09F8EB0 E299A8 F09F97BF F09F8EAA F09F8EAD F09F938D F09F9AA9 F09F87AEF09F87B3 F09F87A9F09F87AA F09F87A7F09F87B7 F09F87B2F09F87BD F09F87AAF09F87B8 F09F87AEF09F87B9 F09F87ACF09F87A7 F09F87B8F09F87A6 F09F87BFF09F87A6 F09F87AEF09F87A9 F09F87A6F09F87B7 F09F87BAF09F87B8 F09F87B9F09F87B7 F09F87B3F09F87B1 F09F87B7F09F87BA F09F87B2F09F87BE F09F87A8F09F87B4 F09F87BBF09F87AA F09F87A8F09F87B1 F09F87B3F09F87AC F09F87ADF09F87B0 F09F87AEF09F87B1 F09F87A8F09F87AD F09F87AAF09F87AC F09F87A6F09F87AA F09F87B8F09F87AC F09F87ABF09F87B7 F09F87A8F09F87A6 F09F87B9F09F87AD F09F87B9F09F87BC F09F87A8F09F87B3 F09F87B0F09F87B7 F09F87AFF09F87B5 F09F87AEF09F87B7 F09F87B8F09F87BE F09F87A6F09F87B9 F09F87B0F09F87BF F09F87B5F09F87AA F09F87AAF09F87A8 F09F87B1F09F87A7 F09F87AFF09F87B4 F09F87A6F09F87BA F09F87A8F09F87B7 F09F87ACF09F87AD F09F87A7F09F87AA F09F87ADF09F87B3 F09F87BAF09F87A6 F09F87B5F09F87B9 F09F87BAF09F87BE F09F87ADF09F87B7 F09F87ACF09F87B7 F09F87A7F09F87A6 F09F87A9F09F87BF F09F87A8F09F87AE F09F87A8F09F87B2</group>
+ <group name="symbols">31E283A3 32E283A3 33E283A3 34E283A3 35E283A3 36E283A3 37E283A3 38E283A3 39E283A3 30E283A3 F09F949F F09F94A2 23E283A3 F09F94A3 E2AC86 E2AC87 E2AC85 E29EA1 F09F94A0 F09F94A1 F09F94A4 E28697 E28696 E28698 E28699 E28694 E28695 F09F9484 E29780 E296B6 F09F94BC F09F94BD E286A9 E286AA E284B9 E28FAA E28FA9 E28FAB E28FAC E2A4B5 E2A4B4 F09F8697 F09F9480 F09F9481 F09F9482 F09F8695 F09F8699 F09F8692 F09F8693 F09F8696 F09F93B6 F09F8EA6 F09F8881 F09F88AF F09F88B3 F09F88B5 F09F88B4 F09F88B2 F09F8990 F09F88B9 F09F88BA F09F88B6 F09F889A F09F9ABB F09F9AB9 F09F9ABA F09F9ABC F09F9ABE F09F9AB0 F09F9AAE F09F85BF E299BF F09F9AAD F09F88B7 F09F88B8 F09F8882 E29382 F09F9B82 F09F9B84 F09F9B85 F09F9B83 F09F8991 E38A99 E38A97 F09F8691 F09F8698 F09F8694 F09F9AAB F09F949E F09F93B5 F09F9AAF F09F9AB1 F09F9AB3 F09F9AB7 F09F9AB8 E29B94 E29CB3 E29D87 E29D8E E29C85 E29CB4 F09F929F F09F869A F09F93B3 F09F93B4 F09F85B0 F09F85B1 F09F868E F09F85BE F09F92A0 E29EBF E299BB E29988 E29989 E2998A E2998B E2998C E2998D E2998E E2998F E29990 E29991 E29992 E29993 E29B8E F09F94AF F09F8FA7 F09F92B9 F09F92B2 F09F92B1 C2A9 C2AE E284A2 E380BD E380B0 F09F949D F09F949A F09F9499 F09F949B F09F949C E29D8C E2AD95 E29D97 E29D93 E29D95 E29D94 F09F9483 F09F959B F09F95A7 F09F9590 F09F959C F09F9591 F09F959D F09F9592 F09F959E F09F9593 F09F959F F09F9594 F09F95A0 F09F9595 F09F9596 F09F9597 F09F9598 F09F9599 F09F959A F09F95A1 F09F95A2 F09F95A3 F09F95A4 F09F95A5 F09F95A6 E29C96 E29E95 E29E96 E29E97 E299A0 E299A5 E299A3 E299A6 F09F92AE F09F92AF E29C94 E29891 F09F9498 F09F9497 E29EB0 F09F94B1 F09F94B2 F09F94B3 E297BC E297BB E297BE E297BD E296AA E296AB F09F94BA E2AC9C E2AC9B E29AAB E29AAA F09F94B4 F09F94B5 F09F94BB F09F94B6 F09F94B7 F09F94B8 F09F94B9</group>
+</emoji> \ No newline at end of file