mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 02:55:23 +00:00
refactor emoji code into package
1) EmojiTextView and EmojiEditText are used instead of using code to emojify text. 2) Emoji categories' code points are specified in XML 3) EmojiDrawer itself is a fragment, and its pages are also fragments, allowing for better memory management. Fixes #2938 Fixes #2936 Closes #3153 // FREEBIE
This commit is contained in:
parent
2bee953560
commit
bf133c65c3
@ -5,7 +5,7 @@
|
|||||||
android:versionCode="118"
|
android:versionCode="118"
|
||||||
android:versionName="2.14.4">
|
android:versionName="2.14.4">
|
||||||
|
|
||||||
<uses-sdk tools:overrideLibrary="com.amulyakhare.textdrawable"/>
|
<uses-sdk tools:overrideLibrary="com.amulyakhare.textdrawable,com.astuetz.pagerslidingtabstrip"/>
|
||||||
|
|
||||||
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
|
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
|
||||||
android:label="Access to TextSecure Secrets"
|
android:label="Access to TextSecure Secrets"
|
||||||
|
@ -37,7 +37,7 @@ dependencies {
|
|||||||
compile 'me.leolin:ShortcutBadger:1.1.0-WS1'
|
compile 'me.leolin:ShortcutBadger:1.1.0-WS1'
|
||||||
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||||
compile 'com.google.android.gms:play-services-base:6.5.87'
|
compile 'com.google.android.gms:play-services-base:6.5.87'
|
||||||
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
compile 'com.jpardogo.materialtabstrip:library:1.0.9'
|
||||||
compile 'org.w3c:smil:1.0.0'
|
compile 'org.w3c:smil:1.0.0'
|
||||||
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
|
compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
|
||||||
compile 'com.github.chrisbanes.photoview:library:1.2.3'
|
compile 'com.github.chrisbanes.photoview:library:1.2.3'
|
||||||
@ -89,7 +89,6 @@ dependencyVerification {
|
|||||||
'me.leolin:ShortcutBadger:3142d017234bfa0cdd69ccded7cc5ea63f13b97574803c8c616c9bbeaad33ad9',
|
'me.leolin:ShortcutBadger:3142d017234bfa0cdd69ccded7cc5ea63f13b97574803c8c616c9bbeaad33ad9',
|
||||||
'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6',
|
'se.emilsjolander:stickylistheaders:89146b46c96fea0e40200474a2625cda10fe94891e4128f53cdb42375091b9b6',
|
||||||
'com.google.android.gms:play-services-base:832cb6b3130e871db6a412c4ab585656dbcc5e7948101f190186757785703f75',
|
'com.google.android.gms:play-services-base:832cb6b3130e871db6a412c4ab585656dbcc5e7948101f190186757785703f75',
|
||||||
'com.astuetz:pagerslidingtabstrip:f1641396732c7132a7abb837e482e5ee2b0ebb8d10813fc52bbaec2c15c184c2',
|
|
||||||
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
|
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
|
||||||
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
|
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
|
||||||
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
|
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:background="?conversation_editor_background">
|
android:background="?conversation_editor_background">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.EmojiToggle
|
<org.thoughtcrime.securesms.components.emoji.EmojiToggle
|
||||||
android:id="@+id/emoji_toggle"
|
android:id="@+id/emoji_toggle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
@ -123,10 +123,8 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<ViewStub android:id="@+id/emoji_drawer_stub"
|
<LinearLayout android:id="@+id/emoji_drawer"
|
||||||
android:inflatedId="@+id/emoji_drawer"
|
android:layout_width="match_parent"
|
||||||
android:layout="@layout/emoji_drawer_stub"
|
android:layout_height="wrap_content" />
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -42,7 +42,8 @@
|
|||||||
android:layout_below="@id/thumbnail_container"
|
android:layout_below="@id/thumbnail_container"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView android:id="@+id/conversation_item_body"
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
android:id="@+id/conversation_item_body"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="5dip"
|
android:paddingTop="5dip"
|
||||||
|
@ -44,7 +44,8 @@
|
|||||||
android:layout_marginRight="12dp"
|
android:layout_marginRight="12dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView android:id="@+id/conversation_item_body"
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
android:id="@+id/conversation_item_body"
|
||||||
android:autoLink="all"
|
android:autoLink="all"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -78,7 +78,8 @@
|
|||||||
android:layout_gravity="top" />
|
android:layout_gravity="top" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView android:id="@+id/subject"
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
android:id="@+id/subject"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -14,7 +15,13 @@
|
|||||||
<com.astuetz.PagerSlidingTabStrip android:id="@+id/tabs"
|
<com.astuetz.PagerSlidingTabStrip android:id="@+id/tabs"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="45dp" />
|
android:layout_height="45dp"
|
||||||
|
app:pstsShouldExpand="true"
|
||||||
|
app:pstsTabPaddingLeftRight="@dimen/emoji_drawer_left_right_padding"
|
||||||
|
app:pstsUnderlineColor="@color/emoji_tab_underline"
|
||||||
|
app:pstsIndicatorColor="@color/emoji_tab_indicator"
|
||||||
|
app:pstsIndicatorHeight="@dimen/emoji_drawer_indicator_height"
|
||||||
|
app:pstsTextAllCaps="false"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.RepeatableImageKey
|
<org.thoughtcrime.securesms.components.RepeatableImageKey
|
||||||
android:id="@+id/backspace"
|
android:id="@+id/backspace"
|
||||||
@ -27,10 +34,11 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager android:id="@+id/emoji_pager"
|
<android.support.v4.view.ViewPager
|
||||||
android:visibility="visible"
|
android:id="@+id/emoji_pager"
|
||||||
android:layout_width="match_parent"
|
android:visibility="visible"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:background="#ff333333" />
|
android:layout_height="match_parent"
|
||||||
|
android:background="#ff333333" />
|
||||||
|
|
||||||
</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout>
|
</org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout>
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<org.thoughtcrime.securesms.components.EmojiDrawer
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1.1" />
|
|
@ -113,5 +113,4 @@
|
|||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<attr name="group_members_dialog_icon" format="reference"/>
|
<attr name="group_members_dialog_icon" format="reference"/>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
867
res/values/emoji.xml
Normal file
867
res/values/emoji.xml
Normal file
@ -0,0 +1,867 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<integer-array name="emoji_smile">
|
||||||
|
<item>0x263a</item>
|
||||||
|
<item>0x1f60a</item>
|
||||||
|
<item>0x1f600</item>
|
||||||
|
<item>0x1f601</item>
|
||||||
|
<item>0x1f602</item>
|
||||||
|
<item>0x1f603</item>
|
||||||
|
<item>0x1f604</item>
|
||||||
|
<item>0x1f605</item>
|
||||||
|
<item>0x1f606</item>
|
||||||
|
<item>0x1f607</item>
|
||||||
|
<item>0x1f608</item>
|
||||||
|
<item>0x1f609</item>
|
||||||
|
<item>0x1f62f</item>
|
||||||
|
<item>0x1f610</item>
|
||||||
|
<item>0x1f611</item>
|
||||||
|
<item>0x1f615</item>
|
||||||
|
<item>0x1f620</item>
|
||||||
|
<item>0x1f62c</item>
|
||||||
|
<item>0x1f621</item>
|
||||||
|
<item>0x1f622</item>
|
||||||
|
<item>0x1f634</item>
|
||||||
|
<item>0x1f62e</item>
|
||||||
|
<item>0x1f623</item>
|
||||||
|
<item>0x1f624</item>
|
||||||
|
<item>0x1f625</item>
|
||||||
|
<item>0x1f626</item>
|
||||||
|
<item>0x1f627</item>
|
||||||
|
<item>0x1f628</item>
|
||||||
|
<item>0x1f629</item>
|
||||||
|
<item>0x1f630</item>
|
||||||
|
<item>0x1f61f</item>
|
||||||
|
<item>0x1f631</item>
|
||||||
|
<item>0x1f632</item>
|
||||||
|
<item>0x1f633</item>
|
||||||
|
<item>0x1f635</item>
|
||||||
|
<item>0x1f636</item>
|
||||||
|
<item>0x1f637</item>
|
||||||
|
<item>0x1f61e</item>
|
||||||
|
<item>0x1f612</item>
|
||||||
|
<item>0x1f60d</item>
|
||||||
|
<item>0x1f61b</item>
|
||||||
|
<item>0x1f61c</item>
|
||||||
|
<item>0x1f61d</item>
|
||||||
|
<item>0x1f60b</item>
|
||||||
|
<item>0x1f617</item>
|
||||||
|
<item>0x1f619</item>
|
||||||
|
<item>0x1f618</item>
|
||||||
|
<item>0x1f61a</item>
|
||||||
|
<item>0x1f60e</item>
|
||||||
|
<item>0x1f62d</item>
|
||||||
|
<item>0x1f60c</item>
|
||||||
|
<item>0x1f616</item>
|
||||||
|
<item>0x1f614</item>
|
||||||
|
<item>0x1f62a</item>
|
||||||
|
<item>0x1f60f</item>
|
||||||
|
<item>0x1f613</item>
|
||||||
|
<item>0x1f62b</item>
|
||||||
|
<item>0x1f64b</item>
|
||||||
|
<item>0x1f64c</item>
|
||||||
|
<item>0x1f64d</item>
|
||||||
|
<item>0x1f645</item>
|
||||||
|
<item>0x1f646</item>
|
||||||
|
<item>0x1f647</item>
|
||||||
|
<item>0x1f64e</item>
|
||||||
|
<item>0x1f64f</item>
|
||||||
|
<item>0x1f63a</item>
|
||||||
|
<item>0x1f63c</item>
|
||||||
|
<item>0x1f638</item>
|
||||||
|
<item>0x1f639</item>
|
||||||
|
<item>0x1f63b</item>
|
||||||
|
<item>0x1f63d</item>
|
||||||
|
<item>0x1f63f</item>
|
||||||
|
<item>0x1f63e</item>
|
||||||
|
<item>0x1f640</item>
|
||||||
|
<item>0x1f648</item>
|
||||||
|
<item>0x1f649</item>
|
||||||
|
<item>0x1f64a</item>
|
||||||
|
<item>0x1f4a9</item>
|
||||||
|
<item>0x1f476</item>
|
||||||
|
<item>0x1f466</item>
|
||||||
|
<item>0x1f467</item>
|
||||||
|
<item>0x1f468</item>
|
||||||
|
<item>0x1f469</item>
|
||||||
|
<item>0x1f474</item>
|
||||||
|
<item>0x1f475</item>
|
||||||
|
<item>0x1f48f</item>
|
||||||
|
<item>0x1f491</item>
|
||||||
|
<item>0x1f46a</item>
|
||||||
|
<item>0x1f46b</item>
|
||||||
|
<item>0x1f46c</item>
|
||||||
|
<item>0x1f46d</item>
|
||||||
|
<item>0x1f464</item>
|
||||||
|
<item>0x1f465</item>
|
||||||
|
<item>0x1f46e</item>
|
||||||
|
<item>0x1f477</item>
|
||||||
|
<item>0x1f481</item>
|
||||||
|
<item>0x1f482</item>
|
||||||
|
<item>0x1f46f</item>
|
||||||
|
<item>0x1f470</item>
|
||||||
|
<item>0x1f478</item>
|
||||||
|
<item>0x1f385</item>
|
||||||
|
<item>0x1f47c</item>
|
||||||
|
<item>0x1f471</item>
|
||||||
|
<item>0x1f472</item>
|
||||||
|
<item>0x1f473</item>
|
||||||
|
<item>0x1f483</item>
|
||||||
|
<item>0x1f486</item>
|
||||||
|
<item>0x1f487</item>
|
||||||
|
<item>0x1f485</item>
|
||||||
|
<item>0x1f47b</item>
|
||||||
|
<item>0x1f479</item>
|
||||||
|
<item>0x1f47a</item>
|
||||||
|
<item>0x1f47d</item>
|
||||||
|
<item>0x1f47e</item>
|
||||||
|
<item>0x1f47f</item>
|
||||||
|
<item>0x1f480</item>
|
||||||
|
<item>0x1f4aa</item>
|
||||||
|
<item>0x1f440</item>
|
||||||
|
<item>0x1f442</item>
|
||||||
|
<item>0x1f443</item>
|
||||||
|
<item>0x1f463</item>
|
||||||
|
<item>0x1f444</item>
|
||||||
|
<item>0x1f445</item>
|
||||||
|
<item>0x1f48b</item>
|
||||||
|
<item>0x2764</item>
|
||||||
|
<item>0x1f499</item>
|
||||||
|
<item>0x1f49a</item>
|
||||||
|
<item>0x1f49b</item>
|
||||||
|
<item>0x1f49c</item>
|
||||||
|
<item>0x1f493</item>
|
||||||
|
<item>0x1f494</item>
|
||||||
|
<item>0x1f495</item>
|
||||||
|
<item>0x1f496</item>
|
||||||
|
<item>0x1f497</item>
|
||||||
|
<item>0x1f498</item>
|
||||||
|
<item>0x1f49d</item>
|
||||||
|
<item>0x1f49e</item>
|
||||||
|
<item>0x1f49f</item>
|
||||||
|
<item>0x1f44d</item>
|
||||||
|
<item>0x1f44e</item>
|
||||||
|
<item>0x1f44c</item>
|
||||||
|
<item>0x270a</item>
|
||||||
|
<item>0x270c</item>
|
||||||
|
<item>0x270b</item>
|
||||||
|
<item>0x1f44a</item>
|
||||||
|
<item>0x261d</item>
|
||||||
|
<item>0x1f446</item>
|
||||||
|
<item>0x1f447</item>
|
||||||
|
<item>0x1f448</item>
|
||||||
|
<item>0x1f449</item>
|
||||||
|
<item>0x1f44b</item>
|
||||||
|
<item>0x1f44f</item>
|
||||||
|
<item>0x1f450</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_flower">
|
||||||
|
<item>0x1f530</item>
|
||||||
|
<item>0x1f484</item>
|
||||||
|
<item>0x1f45e</item>
|
||||||
|
<item>0x1f45f</item>
|
||||||
|
<item>0x1f451</item>
|
||||||
|
<item>0x1f452</item>
|
||||||
|
<item>0x1f3a9</item>
|
||||||
|
<item>0x1f393</item>
|
||||||
|
<item>0x1f453</item>
|
||||||
|
<item>0x231a</item>
|
||||||
|
<item>0x1f454</item>
|
||||||
|
<item>0x1f455</item>
|
||||||
|
<item>0x1f456</item>
|
||||||
|
<item>0x1f457</item>
|
||||||
|
<item>0x1f458</item>
|
||||||
|
<item>0x1f459</item>
|
||||||
|
<item>0x1f460</item>
|
||||||
|
<item>0x1f461</item>
|
||||||
|
<item>0x1f462</item>
|
||||||
|
<item>0x1f45a</item>
|
||||||
|
<item>0x1f45c</item>
|
||||||
|
<item>0x1f4bc</item>
|
||||||
|
<item>0x1f392</item>
|
||||||
|
<item>0x1f45d</item>
|
||||||
|
<item>0x1f45b</item>
|
||||||
|
<item>0x1f4b0</item>
|
||||||
|
<item>0x1f4b3</item>
|
||||||
|
<item>0x1f4b2</item>
|
||||||
|
<item>0x1f4b5</item>
|
||||||
|
<item>0x1f4b4</item>
|
||||||
|
<item>0x1f4b6</item>
|
||||||
|
<item>0x1f4b7</item>
|
||||||
|
<item>0x1f4b8</item>
|
||||||
|
<item>0x1f4b1</item>
|
||||||
|
<item>0x1f4b9</item>
|
||||||
|
<item>0x1f52b</item>
|
||||||
|
<item>0x1f52a</item>
|
||||||
|
<item>0x1f4a3</item>
|
||||||
|
<item>0x1f489</item>
|
||||||
|
<item>0x1f48a</item>
|
||||||
|
<item>0x1f6ac</item>
|
||||||
|
<item>0x1f514</item>
|
||||||
|
<item>0x1f515</item>
|
||||||
|
<item>0x1f6aa</item>
|
||||||
|
<item>0x1f52c</item>
|
||||||
|
<item>0x1f52d</item>
|
||||||
|
<item>0x1f52e</item>
|
||||||
|
<item>0x1f526</item>
|
||||||
|
<item>0x1f50b</item>
|
||||||
|
<item>0x1f50c</item>
|
||||||
|
<item>0x1f4dc</item>
|
||||||
|
<item>0x1f4d7</item>
|
||||||
|
<item>0x1f4d8</item>
|
||||||
|
<item>0x1f4d9</item>
|
||||||
|
<item>0x1f4da</item>
|
||||||
|
<item>0x1f4d4</item>
|
||||||
|
<item>0x1f4d2</item>
|
||||||
|
<item>0x1f4d1</item>
|
||||||
|
<item>0x1f4d3</item>
|
||||||
|
<item>0x1f4d5</item>
|
||||||
|
<item>0x1f4d6</item>
|
||||||
|
<item>0x1f4f0</item>
|
||||||
|
<item>0x1f4db</item>
|
||||||
|
<item>0x1f383</item>
|
||||||
|
<item>0x1f384</item>
|
||||||
|
<item>0x1f380</item>
|
||||||
|
<item>0x1f381</item>
|
||||||
|
<item>0x1f382</item>
|
||||||
|
<item>0x1f388</item>
|
||||||
|
<item>0x1f386</item>
|
||||||
|
<item>0x1f387</item>
|
||||||
|
<item>0x1f389</item>
|
||||||
|
<item>0x1f38a</item>
|
||||||
|
<item>0x1f38d</item>
|
||||||
|
<item>0x1f38f</item>
|
||||||
|
<item>0x1f38c</item>
|
||||||
|
<item>0x1f390</item>
|
||||||
|
<item>0x1f38b</item>
|
||||||
|
<item>0x1f38e</item>
|
||||||
|
<item>0x1f4f1</item>
|
||||||
|
<item>0x1f4f2</item>
|
||||||
|
<item>0x1f4df</item>
|
||||||
|
<item>0x260e</item>
|
||||||
|
<item>0x1f4de</item>
|
||||||
|
<item>0x1f4e0</item>
|
||||||
|
<item>0x1f4e6</item>
|
||||||
|
<item>0x2709</item>
|
||||||
|
<item>0x1f4e8</item>
|
||||||
|
<item>0x1f4e9</item>
|
||||||
|
<item>0x1f4ea</item>
|
||||||
|
<item>0x1f4eb</item>
|
||||||
|
<item>0x1f4ed</item>
|
||||||
|
<item>0x1f4ec</item>
|
||||||
|
<item>0x1f4ee</item>
|
||||||
|
<item>0x1f4e4</item>
|
||||||
|
<item>0x1f4e5</item>
|
||||||
|
<item>0x1f4ef</item>
|
||||||
|
<item>0x1f4e2</item>
|
||||||
|
<item>0x1f4e3</item>
|
||||||
|
<item>0x1f4e1</item>
|
||||||
|
<item>0x1f4ac</item>
|
||||||
|
<item>0x1f4ad</item>
|
||||||
|
<item>0x2712</item>
|
||||||
|
<item>0x270f</item>
|
||||||
|
<item>0x1f4dd</item>
|
||||||
|
<item>0x1f4cf</item>
|
||||||
|
<item>0x1f4d0</item>
|
||||||
|
<item>0x1f4cd</item>
|
||||||
|
<item>0x1f4cc</item>
|
||||||
|
<item>0x1f4ce</item>
|
||||||
|
<item>0x2702</item>
|
||||||
|
<item>0x1f4ba</item>
|
||||||
|
<item>0x1f4bb</item>
|
||||||
|
<item>0x1f4bd</item>
|
||||||
|
<item>0x1f4be</item>
|
||||||
|
<item>0x1f4bf</item>
|
||||||
|
<item>0x1f4c6</item>
|
||||||
|
<item>0x1f4c5</item>
|
||||||
|
<item>0x1f4c7</item>
|
||||||
|
<item>0x1f4cb</item>
|
||||||
|
<item>0x1f4c1</item>
|
||||||
|
<item>0x1f4c2</item>
|
||||||
|
<item>0x1f4c3</item>
|
||||||
|
<item>0x1f4c4</item>
|
||||||
|
<item>0x1f4ca</item>
|
||||||
|
<item>0x1f4c8</item>
|
||||||
|
<item>0x1f4c9</item>
|
||||||
|
<item>0x26fa</item>
|
||||||
|
<item>0x1f3a1</item>
|
||||||
|
<item>0x1f3a2</item>
|
||||||
|
<item>0x1f3a0</item>
|
||||||
|
<item>0x1f3aa</item>
|
||||||
|
<item>0x1f3a8</item>
|
||||||
|
<item>0x1f3ac</item>
|
||||||
|
<item>0x1f3a5</item>
|
||||||
|
<item>0x1f4f7</item>
|
||||||
|
<item>0x1f4f9</item>
|
||||||
|
<item>0x1f3a6</item>
|
||||||
|
<item>0x1f3ad</item>
|
||||||
|
<item>0x1f3ab</item>
|
||||||
|
<item>0x1f3ae</item>
|
||||||
|
<item>0x1f3b2</item>
|
||||||
|
<item>0x1f3b0</item>
|
||||||
|
<item>0x1f0cf</item>
|
||||||
|
<item>0x1f3b4</item>
|
||||||
|
<item>0x1f004</item>
|
||||||
|
<item>0x1f3af</item>
|
||||||
|
<item>0x1f4fa</item>
|
||||||
|
<item>0x1f4fb</item>
|
||||||
|
<item>0x1f4c0</item>
|
||||||
|
<item>0x1f4fc</item>
|
||||||
|
<item>0x1f3a7</item>
|
||||||
|
<item>0x1f3a4</item>
|
||||||
|
<item>0x1f3b5</item>
|
||||||
|
<item>0x1f3b6</item>
|
||||||
|
<item>0x1f3bc</item>
|
||||||
|
<item>0x1f3bb</item>
|
||||||
|
<item>0x1f3b9</item>
|
||||||
|
<item>0x1f3b7</item>
|
||||||
|
<item>0x1f3ba</item>
|
||||||
|
<item>0x1f3b8</item>
|
||||||
|
<item>0x303d</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_bell">
|
||||||
|
<item>0x1f415</item>
|
||||||
|
<item>0x1f436</item>
|
||||||
|
<item>0x1f429</item>
|
||||||
|
<item>0x1f408</item>
|
||||||
|
<item>0x1f431</item>
|
||||||
|
<item>0x1f400</item>
|
||||||
|
<item>0x1f401</item>
|
||||||
|
<item>0x1f42d</item>
|
||||||
|
<item>0x1f439</item>
|
||||||
|
<item>0x1f422</item>
|
||||||
|
<item>0x1f407</item>
|
||||||
|
<item>0x1f430</item>
|
||||||
|
<item>0x1f413</item>
|
||||||
|
<item>0x1f414</item>
|
||||||
|
<item>0x1f423</item>
|
||||||
|
<item>0x1f424</item>
|
||||||
|
<item>0x1f425</item>
|
||||||
|
<item>0x1f426</item>
|
||||||
|
<item>0x1f40f</item>
|
||||||
|
<item>0x1f411</item>
|
||||||
|
<item>0x1f410</item>
|
||||||
|
<item>0x1f43a</item>
|
||||||
|
<item>0x1f403</item>
|
||||||
|
<item>0x1f402</item>
|
||||||
|
<item>0x1f404</item>
|
||||||
|
<item>0x1f42e</item>
|
||||||
|
<item>0x1f434</item>
|
||||||
|
<item>0x1f417</item>
|
||||||
|
<item>0x1f416</item>
|
||||||
|
<item>0x1f437</item>
|
||||||
|
<item>0x1f43d</item>
|
||||||
|
<item>0x1f438</item>
|
||||||
|
<item>0x1f40d</item>
|
||||||
|
<item>0x1f43c</item>
|
||||||
|
<item>0x1f427</item>
|
||||||
|
<item>0x1f418</item>
|
||||||
|
<item>0x1f428</item>
|
||||||
|
<item>0x1f412</item>
|
||||||
|
<item>0x1f435</item>
|
||||||
|
<item>0x1f406</item>
|
||||||
|
<item>0x1f42f</item>
|
||||||
|
<item>0x1f43b</item>
|
||||||
|
<item>0x1f42b</item>
|
||||||
|
<item>0x1f42a</item>
|
||||||
|
<item>0x1f40a</item>
|
||||||
|
<item>0x1f433</item>
|
||||||
|
<item>0x1f40b</item>
|
||||||
|
<item>0x1f41f</item>
|
||||||
|
<item>0x1f420</item>
|
||||||
|
<item>0x1f421</item>
|
||||||
|
<item>0x1f419</item>
|
||||||
|
<item>0x1f41a</item>
|
||||||
|
<item>0x1f42c</item>
|
||||||
|
<item>0x1f40c</item>
|
||||||
|
<item>0x1f41b</item>
|
||||||
|
<item>0x1f41c</item>
|
||||||
|
<item>0x1f41d</item>
|
||||||
|
<item>0x1f41e</item>
|
||||||
|
<item>0x1f432</item>
|
||||||
|
<item>0x1f409</item>
|
||||||
|
<item>0x1f43e</item>
|
||||||
|
<item>0x1f378</item>
|
||||||
|
<item>0x1f37a</item>
|
||||||
|
<item>0x1f37b</item>
|
||||||
|
<item>0x1f377</item>
|
||||||
|
<item>0x1f379</item>
|
||||||
|
<item>0x1f376</item>
|
||||||
|
<item>0x2615</item>
|
||||||
|
<item>0x1f375</item>
|
||||||
|
<item>0x1f37c</item>
|
||||||
|
<item>0x1f374</item>
|
||||||
|
<item>0x1f368</item>
|
||||||
|
<item>0x1f367</item>
|
||||||
|
<item>0x1f366</item>
|
||||||
|
<item>0x1f369</item>
|
||||||
|
<item>0x1f370</item>
|
||||||
|
<item>0x1f36a</item>
|
||||||
|
<item>0x1f36b</item>
|
||||||
|
<item>0x1f36c</item>
|
||||||
|
<item>0x1f36d</item>
|
||||||
|
<item>0x1f36e</item>
|
||||||
|
<item>0x1f36f</item>
|
||||||
|
<item>0x1f373</item>
|
||||||
|
<item>0x1f354</item>
|
||||||
|
<item>0x1f35f</item>
|
||||||
|
<item>0x1f35d</item>
|
||||||
|
<item>0x1f355</item>
|
||||||
|
<item>0x1f356</item>
|
||||||
|
<item>0x1f357</item>
|
||||||
|
<item>0x1f364</item>
|
||||||
|
<item>0x1f363</item>
|
||||||
|
<item>0x1f371</item>
|
||||||
|
<item>0x1f35e</item>
|
||||||
|
<item>0x1f35c</item>
|
||||||
|
<item>0x1f359</item>
|
||||||
|
<item>0x1f35a</item>
|
||||||
|
<item>0x1f35b</item>
|
||||||
|
<item>0x1f372</item>
|
||||||
|
<item>0x1f365</item>
|
||||||
|
<item>0x1f362</item>
|
||||||
|
<item>0x1f361</item>
|
||||||
|
<item>0x1f358</item>
|
||||||
|
<item>0x1f360</item>
|
||||||
|
<item>0x1f34c</item>
|
||||||
|
<item>0x1f34e</item>
|
||||||
|
<item>0x1f34f</item>
|
||||||
|
<item>0x1f34a</item>
|
||||||
|
<item>0x1f34b</item>
|
||||||
|
<item>0x1f344</item>
|
||||||
|
<item>0x1f345</item>
|
||||||
|
<item>0x1f346</item>
|
||||||
|
<item>0x1f347</item>
|
||||||
|
<item>0x1f348</item>
|
||||||
|
<item>0x1f349</item>
|
||||||
|
<item>0x1f350</item>
|
||||||
|
<item>0x1f351</item>
|
||||||
|
<item>0x1f352</item>
|
||||||
|
<item>0x1f353</item>
|
||||||
|
<item>0x1f34d</item>
|
||||||
|
<item>0x1f330</item>
|
||||||
|
<item>0x1f331</item>
|
||||||
|
<item>0x1f332</item>
|
||||||
|
<item>0x1f333</item>
|
||||||
|
<item>0x1f334</item>
|
||||||
|
<item>0x1f335</item>
|
||||||
|
<item>0x1f337</item>
|
||||||
|
<item>0x1f338</item>
|
||||||
|
<item>0x1f339</item>
|
||||||
|
<item>0x1f340</item>
|
||||||
|
<item>0x1f341</item>
|
||||||
|
<item>0x1f342</item>
|
||||||
|
<item>0x1f343</item>
|
||||||
|
<item>0x1f33a</item>
|
||||||
|
<item>0x1f33b</item>
|
||||||
|
<item>0x1f33c</item>
|
||||||
|
<item>0x1f33d</item>
|
||||||
|
<item>0x1f33e</item>
|
||||||
|
<item>0x1f33f</item>
|
||||||
|
<item>0x2600</item>
|
||||||
|
<item>0x1f308</item>
|
||||||
|
<item>0x26c5</item>
|
||||||
|
<item>0x2601</item>
|
||||||
|
<item>0x1f301</item>
|
||||||
|
<item>0x1f302</item>
|
||||||
|
<item>0x2614</item>
|
||||||
|
<item>0x1f4a7</item>
|
||||||
|
<item>0x26a1</item>
|
||||||
|
<item>0x1f300</item>
|
||||||
|
<item>0x2744</item>
|
||||||
|
<item>0x26c4</item>
|
||||||
|
<item>0x1f319</item>
|
||||||
|
<item>0x1f31e</item>
|
||||||
|
<item>0x1f31d</item>
|
||||||
|
<item>0x1f31a</item>
|
||||||
|
<item>0x1f31b</item>
|
||||||
|
<item>0x1f31c</item>
|
||||||
|
<item>0x1f311</item>
|
||||||
|
<item>0x1f312</item>
|
||||||
|
<item>0x1f313</item>
|
||||||
|
<item>0x1f314</item>
|
||||||
|
<item>0x1f315</item>
|
||||||
|
<item>0x1f316</item>
|
||||||
|
<item>0x1f317</item>
|
||||||
|
<item>0x1f318</item>
|
||||||
|
<item>0x1f391</item>
|
||||||
|
<item>0x1f304</item>
|
||||||
|
<item>0x1f305</item>
|
||||||
|
<item>0x1f307</item>
|
||||||
|
<item>0x1f306</item>
|
||||||
|
<item>0x1f303</item>
|
||||||
|
<item>0x1f30c</item>
|
||||||
|
<item>0x1f309</item>
|
||||||
|
<item>0x1f30a</item>
|
||||||
|
<item>0x1f30b</item>
|
||||||
|
<item>0x1f30e</item>
|
||||||
|
<item>0x1f30f</item>
|
||||||
|
<item>0x1f30d</item>
|
||||||
|
<item>0x1f310</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_car">
|
||||||
|
<item>0x1f3e0</item>
|
||||||
|
<item>0x1f3e1</item>
|
||||||
|
<item>0x1f3e2</item>
|
||||||
|
<item>0x1f3e3</item>
|
||||||
|
<item>0x1f3e4</item>
|
||||||
|
<item>0x1f3e5</item>
|
||||||
|
<item>0x1f3e6</item>
|
||||||
|
<item>0x1f3e7</item>
|
||||||
|
<item>0x1f3e8</item>
|
||||||
|
<item>0x1f3e9</item>
|
||||||
|
<item>0x1f3ea</item>
|
||||||
|
<item>0x1f3eb</item>
|
||||||
|
<item>0x26ea</item>
|
||||||
|
<item>0x26f2</item>
|
||||||
|
<item>0x1f3ec</item>
|
||||||
|
<item>0x1f3ef</item>
|
||||||
|
<item>0x1f3f0</item>
|
||||||
|
<item>0x1f3ed</item>
|
||||||
|
<item>0x1f5fb</item>
|
||||||
|
<item>0x1f5fc</item>
|
||||||
|
<item>0x1f5fd</item>
|
||||||
|
<item>0x1f5fe</item>
|
||||||
|
<item>0x1f5ff</item>
|
||||||
|
<item>0x2693</item>
|
||||||
|
<item>0x1f3ee</item>
|
||||||
|
<item>0x1f488</item>
|
||||||
|
<item>0x1f527</item>
|
||||||
|
<item>0x1f528</item>
|
||||||
|
<item>0x1f529</item>
|
||||||
|
<item>0x1f6bf</item>
|
||||||
|
<item>0x1f6c1</item>
|
||||||
|
<item>0x1f6c0</item>
|
||||||
|
<item>0x1f6bd</item>
|
||||||
|
<item>0x1f6be</item>
|
||||||
|
<item>0x1f3bd</item>
|
||||||
|
<item>0x1f3a3</item>
|
||||||
|
<item>0x1f3b1</item>
|
||||||
|
<item>0x1f3b3</item>
|
||||||
|
<item>0x26be</item>
|
||||||
|
<item>0x26f3</item>
|
||||||
|
<item>0x1f3be</item>
|
||||||
|
<item>0x26bd</item>
|
||||||
|
<item>0x1f3bf</item>
|
||||||
|
<item>0x1f3c0</item>
|
||||||
|
<item>0x1f3c1</item>
|
||||||
|
<item>0x1f3c2</item>
|
||||||
|
<item>0x1f3c3</item>
|
||||||
|
<item>0x1f3c4</item>
|
||||||
|
<item>0x1f3c6</item>
|
||||||
|
<item>0x1f3c7</item>
|
||||||
|
<item>0x1f40e</item>
|
||||||
|
<item>0x1f3c8</item>
|
||||||
|
<item>0x1f3c9</item>
|
||||||
|
<item>0x1f3ca</item>
|
||||||
|
<item>0x1f682</item>
|
||||||
|
<item>0x1f683</item>
|
||||||
|
<item>0x1f684</item>
|
||||||
|
<item>0x1f685</item>
|
||||||
|
<item>0x1f686</item>
|
||||||
|
<item>0x1f687</item>
|
||||||
|
<item>0x24c2</item>
|
||||||
|
<item>0x1f688</item>
|
||||||
|
<item>0x1f68a</item>
|
||||||
|
<item>0x1f68b</item>
|
||||||
|
<item>0x1f68c</item>
|
||||||
|
<item>0x1f68d</item>
|
||||||
|
<item>0x1f68e</item>
|
||||||
|
<item>0x1f68f</item>
|
||||||
|
<item>0x1f690</item>
|
||||||
|
<item>0x1f691</item>
|
||||||
|
<item>0x1f692</item>
|
||||||
|
<item>0x1f693</item>
|
||||||
|
<item>0x1f694</item>
|
||||||
|
<item>0x1f695</item>
|
||||||
|
<item>0x1f696</item>
|
||||||
|
<item>0x1f697</item>
|
||||||
|
<item>0x1f698</item>
|
||||||
|
<item>0x1f699</item>
|
||||||
|
<item>0x1f69a</item>
|
||||||
|
<item>0x1f69b</item>
|
||||||
|
<item>0x1f69c</item>
|
||||||
|
<item>0x1f69d</item>
|
||||||
|
<item>0x1f69e</item>
|
||||||
|
<item>0x1f69f</item>
|
||||||
|
<item>0x1f6a0</item>
|
||||||
|
<item>0x1f6a1</item>
|
||||||
|
<item>0x1f6a2</item>
|
||||||
|
<item>0x1f6a3</item>
|
||||||
|
<item>0x1f681</item>
|
||||||
|
<item>0x2708</item>
|
||||||
|
<item>0x1f6c2</item>
|
||||||
|
<item>0x1f6c3</item>
|
||||||
|
<item>0x1f6c4</item>
|
||||||
|
<item>0x1f6c5</item>
|
||||||
|
<item>0x26f5</item>
|
||||||
|
<item>0x1f6b2</item>
|
||||||
|
<item>0x1f6b3</item>
|
||||||
|
<item>0x1f6b4</item>
|
||||||
|
<item>0x1f6b5</item>
|
||||||
|
<item>0x1f6b7</item>
|
||||||
|
<item>0x1f6b8</item>
|
||||||
|
<item>0x1f689</item>
|
||||||
|
<item>0x1f680</item>
|
||||||
|
<item>0x1f6a4</item>
|
||||||
|
<item>0x1f6b6</item>
|
||||||
|
<item>0x26fd</item>
|
||||||
|
<item>0x1f17f</item>
|
||||||
|
<item>0x1f6a5</item>
|
||||||
|
<item>0x1f6a6</item>
|
||||||
|
<item>0x1f6a7</item>
|
||||||
|
<item>0x1f6a8</item>
|
||||||
|
<item>0x2668</item>
|
||||||
|
<item>0x1f48c</item>
|
||||||
|
<item>0x1f48d</item>
|
||||||
|
<item>0x1f48e</item>
|
||||||
|
<item>0x1f490</item>
|
||||||
|
<item>0x1f492</item>
|
||||||
|
<item>0xfe4e5</item>
|
||||||
|
<item>0xfe4e6</item>
|
||||||
|
<item>0xfe4e7</item>
|
||||||
|
<item>0xfe4e8</item>
|
||||||
|
<item>0xfe4e9</item>
|
||||||
|
<item>0xfe4ea</item>
|
||||||
|
<item>0xfe4eb</item>
|
||||||
|
<item>0xfe4ec</item>
|
||||||
|
<item>0xfe4ed</item>
|
||||||
|
<item>0xfe4ee</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_symbol">
|
||||||
|
<item>0x1f51d</item>
|
||||||
|
<item>0x1f519</item>
|
||||||
|
<item>0x1f51b</item>
|
||||||
|
<item>0x1f51c</item>
|
||||||
|
<item>0x1f51a</item>
|
||||||
|
<item>0x23f3</item>
|
||||||
|
<item>0x231b</item>
|
||||||
|
<item>0x23f0</item>
|
||||||
|
<item>0x2648</item>
|
||||||
|
<item>0x2649</item>
|
||||||
|
<item>0x264a</item>
|
||||||
|
<item>0x264b</item>
|
||||||
|
<item>0x264c</item>
|
||||||
|
<item>0x264d</item>
|
||||||
|
<item>0x264e</item>
|
||||||
|
<item>0x264f</item>
|
||||||
|
<item>0x2650</item>
|
||||||
|
<item>0x2651</item>
|
||||||
|
<item>0x2652</item>
|
||||||
|
<item>0x2653</item>
|
||||||
|
<item>0x26ce</item>
|
||||||
|
<item>0x1f531</item>
|
||||||
|
<item>0x1f52f</item>
|
||||||
|
<item>0x1f6bb</item>
|
||||||
|
<item>0x1f6ae</item>
|
||||||
|
<item>0x1f6af</item>
|
||||||
|
<item>0x1f6b0</item>
|
||||||
|
<item>0x1f6b1</item>
|
||||||
|
<item>0x1f170</item>
|
||||||
|
<item>0x1f171</item>
|
||||||
|
<item>0x1f18e</item>
|
||||||
|
<item>0x1f17e</item>
|
||||||
|
<item>0x1f4ae</item>
|
||||||
|
<item>0x1f4af</item>
|
||||||
|
<item>0x1f520</item>
|
||||||
|
<item>0x1f521</item>
|
||||||
|
<item>0x1f522</item>
|
||||||
|
<item>0x1f523</item>
|
||||||
|
<item>0x1f524</item>
|
||||||
|
<item>0x27bf</item>
|
||||||
|
<item>0x1f4f6</item>
|
||||||
|
<item>0x1f4f3</item>
|
||||||
|
<item>0x1f4f4</item>
|
||||||
|
<item>0x1f4f5</item>
|
||||||
|
<item>0x1f6b9</item>
|
||||||
|
<item>0x1f6ba</item>
|
||||||
|
<item>0x1f6bc</item>
|
||||||
|
<item>0x267f</item>
|
||||||
|
<item>0x267b</item>
|
||||||
|
<item>0x1f6ad</item>
|
||||||
|
<item>0x1f6a9</item>
|
||||||
|
<item>0x26a0</item>
|
||||||
|
<item>0x1f201</item>
|
||||||
|
<item>0x1f51e</item>
|
||||||
|
<item>0x26d4</item>
|
||||||
|
<item>0x1f192</item>
|
||||||
|
<item>0x1f197</item>
|
||||||
|
<item>0x1f195</item>
|
||||||
|
<item>0x1f198</item>
|
||||||
|
<item>0x1f199</item>
|
||||||
|
<item>0x1f193</item>
|
||||||
|
<item>0x1f196</item>
|
||||||
|
<item>0x1f19a</item>
|
||||||
|
<item>0x1f232</item>
|
||||||
|
<item>0x1f233</item>
|
||||||
|
<item>0x1f234</item>
|
||||||
|
<item>0x1f235</item>
|
||||||
|
<item>0x1f236</item>
|
||||||
|
<item>0x1f237</item>
|
||||||
|
<item>0x1f238</item>
|
||||||
|
<item>0x1f239</item>
|
||||||
|
<item>0x1f202</item>
|
||||||
|
<item>0x1f23a</item>
|
||||||
|
<item>0x1f250</item>
|
||||||
|
<item>0x1f251</item>
|
||||||
|
<item>0x3299</item>
|
||||||
|
<item>0x00ae</item>
|
||||||
|
<item>0x00a9</item>
|
||||||
|
<item>0x2122</item>
|
||||||
|
<item>0x1f21a</item>
|
||||||
|
<item>0x1f22f</item>
|
||||||
|
<item>0x3297</item>
|
||||||
|
<item>0x2b55</item>
|
||||||
|
<item>0x274c</item>
|
||||||
|
<item>0x274e</item>
|
||||||
|
<item>0x2139</item>
|
||||||
|
<item>0x1f6ab</item>
|
||||||
|
<item>0x2705</item>
|
||||||
|
<item>0x2714</item>
|
||||||
|
<item>0x1f517</item>
|
||||||
|
<item>0x2734</item>
|
||||||
|
<item>0x2733</item>
|
||||||
|
<item>0x2795</item>
|
||||||
|
<item>0x2796</item>
|
||||||
|
<item>0x2716</item>
|
||||||
|
<item>0x2797</item>
|
||||||
|
<item>0x1f4a0</item>
|
||||||
|
<item>0x1f4a1</item>
|
||||||
|
<item>0x1f4a4</item>
|
||||||
|
<item>0x1f4a2</item>
|
||||||
|
<item>0x1f525</item>
|
||||||
|
<item>0x1f4a5</item>
|
||||||
|
<item>0x1f4a8</item>
|
||||||
|
<item>0x1f4a6</item>
|
||||||
|
<item>0x1f4ab</item>
|
||||||
|
<item>0x1f55b</item>
|
||||||
|
<item>0x1f567</item>
|
||||||
|
<item>0x1f550</item>
|
||||||
|
<item>0x1f55c</item>
|
||||||
|
<item>0x1f551</item>
|
||||||
|
<item>0x1f55d</item>
|
||||||
|
<item>0x1f552</item>
|
||||||
|
<item>0x1f55e</item>
|
||||||
|
<item>0x1f553</item>
|
||||||
|
<item>0x1f55f</item>
|
||||||
|
<item>0x1f554</item>
|
||||||
|
<item>0x1f560</item>
|
||||||
|
<item>0x1f555</item>
|
||||||
|
<item>0x1f561</item>
|
||||||
|
<item>0x1f556</item>
|
||||||
|
<item>0x1f562</item>
|
||||||
|
<item>0x1f557</item>
|
||||||
|
<item>0x1f563</item>
|
||||||
|
<item>0x1f558</item>
|
||||||
|
<item>0x1f564</item>
|
||||||
|
<item>0x1f559</item>
|
||||||
|
<item>0x1f565</item>
|
||||||
|
<item>0x1f55a</item>
|
||||||
|
<item>0x1f566</item>
|
||||||
|
<item>0x2195</item>
|
||||||
|
<item>0x2b06</item>
|
||||||
|
<item>0x2197</item>
|
||||||
|
<item>0x27a1</item>
|
||||||
|
<item>0x2198</item>
|
||||||
|
<item>0x2b07</item>
|
||||||
|
<item>0x2199</item>
|
||||||
|
<item>0x2b05</item>
|
||||||
|
<item>0x2196</item>
|
||||||
|
<item>0x2194</item>
|
||||||
|
<item>0x2934</item>
|
||||||
|
<item>0x2935</item>
|
||||||
|
<item>0x23ea</item>
|
||||||
|
<item>0x23eb</item>
|
||||||
|
<item>0x23ec</item>
|
||||||
|
<item>0x23e9</item>
|
||||||
|
<item>0x25c0</item>
|
||||||
|
<item>0x25b6</item>
|
||||||
|
<item>0x1f53d</item>
|
||||||
|
<item>0x1f53c</item>
|
||||||
|
<item>0x2747</item>
|
||||||
|
<item>0x2728</item>
|
||||||
|
<item>0x1f534</item>
|
||||||
|
<item>0x1f535</item>
|
||||||
|
<item>0x26aa</item>
|
||||||
|
<item>0x26ab</item>
|
||||||
|
<item>0x1f533</item>
|
||||||
|
<item>0x1f532</item>
|
||||||
|
<item>0x2b50</item>
|
||||||
|
<item>0x1f31f</item>
|
||||||
|
<item>0x1f320</item>
|
||||||
|
<item>0x25ab</item>
|
||||||
|
<item>0x25aa</item>
|
||||||
|
<item>0x25fd</item>
|
||||||
|
<item>0x25fe</item>
|
||||||
|
<item>0x25fb</item>
|
||||||
|
<item>0x25fc</item>
|
||||||
|
<item>0x2b1c</item>
|
||||||
|
<item>0x2b1b</item>
|
||||||
|
<item>0x1f538</item>
|
||||||
|
<item>0x1f539</item>
|
||||||
|
<item>0x1f536</item>
|
||||||
|
<item>0x1f537</item>
|
||||||
|
<item>0x1f53a</item>
|
||||||
|
<item>0x1f53b</item>
|
||||||
|
<item>0x1f51f</item>
|
||||||
|
<!--<item>0x20e3</item>-->
|
||||||
|
<item>0x2754</item>
|
||||||
|
<item>0x2753</item>
|
||||||
|
<item>0x2755</item>
|
||||||
|
<item>0x2757</item>
|
||||||
|
<item>0x203c</item>
|
||||||
|
<item>0x2049</item>
|
||||||
|
<item>0x3030</item>
|
||||||
|
<item>0x27b0</item>
|
||||||
|
<item>0x2660</item>
|
||||||
|
<item>0x2665</item>
|
||||||
|
<item>0x2663</item>
|
||||||
|
<item>0x2666</item>
|
||||||
|
<item>0x1f194</item>
|
||||||
|
<item>0x1f511</item>
|
||||||
|
<item>0x21a9</item>
|
||||||
|
<item>0x1f191</item>
|
||||||
|
<item>0x1f50d</item>
|
||||||
|
<item>0x1f512</item>
|
||||||
|
<item>0x1f513</item>
|
||||||
|
<item>0x21aa</item>
|
||||||
|
<item>0x1f510</item>
|
||||||
|
<item>0x2611</item>
|
||||||
|
<item>0x1f518</item>
|
||||||
|
<item>0x1f50e</item>
|
||||||
|
<item>0x1f516</item>
|
||||||
|
<item>0x1f50f</item>
|
||||||
|
<item>0x1f503</item>
|
||||||
|
<item>0x1f500</item>
|
||||||
|
<item>0x1f501</item>
|
||||||
|
<item>0x1f502</item>
|
||||||
|
<item>0x1f504</item>
|
||||||
|
<item>0x1f4e7</item>
|
||||||
|
<item>0x1f505</item>
|
||||||
|
<item>0x1f506</item>
|
||||||
|
<item>0x1f507</item>
|
||||||
|
<item>0x1f508</item>
|
||||||
|
<item>0x1f509</item>
|
||||||
|
<item>0x1f50a</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_categories">
|
||||||
|
<item>@array/emoji_smile</item>
|
||||||
|
<item>@array/emoji_flower</item>
|
||||||
|
<item>@array/emoji_bell</item>
|
||||||
|
<item>@array/emoji_car</item>
|
||||||
|
<item>@array/emoji_symbol</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="emoji_category_icons">
|
||||||
|
<item>@drawable/emoji_category_smile</item>
|
||||||
|
<item>@drawable/emoji_category_flower</item>
|
||||||
|
<item>@drawable/emoji_category_bell</item>
|
||||||
|
<item>@drawable/emoji_category_car</item>
|
||||||
|
<item>@drawable/emoji_category_symbol</item>
|
||||||
|
</integer-array>
|
||||||
|
</resources>
|
@ -42,7 +42,6 @@ import android.view.View;
|
|||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.View.OnFocusChangeListener;
|
import android.view.View.OnFocusChangeListener;
|
||||||
import android.view.View.OnKeyListener;
|
import android.view.View.OnKeyListener;
|
||||||
import android.view.ViewStub;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@ -53,8 +52,9 @@ import com.google.protobuf.ByteString;
|
|||||||
|
|
||||||
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
|
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
|
||||||
import org.thoughtcrime.securesms.components.ComposeText;
|
import org.thoughtcrime.securesms.components.ComposeText;
|
||||||
import org.thoughtcrime.securesms.components.EmojiDrawer;
|
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
|
||||||
import org.thoughtcrime.securesms.components.EmojiToggle;
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||||
import org.thoughtcrime.securesms.components.SendButton;
|
import org.thoughtcrime.securesms.components.SendButton;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||||
@ -93,7 +93,6 @@ import org.thoughtcrime.securesms.util.Dialogs;
|
|||||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -323,8 +322,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (emojiDrawer.isPresent() && emojiDrawer.get().getVisibility() == View.VISIBLE) {
|
if (isEmojiDrawerOpen()) {
|
||||||
emojiDrawer.get().setVisibility(View.GONE);
|
getEmojiDrawer().hide();
|
||||||
emojiToggle.toggle();
|
emojiToggle.toggle();
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
@ -621,15 +620,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(List<Draft> drafts) {
|
protected void onPostExecute(List<Draft> drafts) {
|
||||||
boolean nativeEmojiSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
|
||||||
Context context = ConversationActivity.this;
|
|
||||||
|
|
||||||
for (Draft draft : drafts) {
|
for (Draft draft : drafts) {
|
||||||
if (draft.getType().equals(Draft.TEXT) && !nativeEmojiSupported) {
|
if (draft.getType().equals(Draft.TEXT)) {
|
||||||
composeText.setText(Emoji.getInstance(context).emojify(draft.getValue(),
|
|
||||||
new Emoji.InvalidatingPageLoadedListener(composeText)),
|
|
||||||
TextView.BufferType.SPANNABLE);
|
|
||||||
} else if (draft.getType().equals(Draft.TEXT)) {
|
|
||||||
composeText.setText(draft.getValue());
|
composeText.setText(draft.getValue());
|
||||||
} else if (draft.getType().equals(Draft.IMAGE)) {
|
} else if (draft.getType().equals(Draft.IMAGE)) {
|
||||||
addAttachmentImage(Uri.parse(draft.getValue()));
|
addAttachmentImage(Uri.parse(draft.getValue()));
|
||||||
@ -713,10 +705,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
emojiToggle.setOnClickListener(new EmojiToggleListener());
|
emojiToggle.setOnClickListener(new EmojiToggleListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
private EmojiDrawer initializeEmojiDrawer() {
|
private EmojiDrawer getEmojiDrawer() {
|
||||||
EmojiDrawer emojiDrawer = (EmojiDrawer)((ViewStub)findViewById(R.id.emoji_drawer_stub)).inflate();
|
if (emojiDrawer.isPresent()) return emojiDrawer.get();
|
||||||
emojiDrawer.setComposeEditText(composeText);
|
|
||||||
return emojiDrawer;
|
EmojiDrawer emojiDrawerFragment = EmojiDrawer.newInstance();
|
||||||
|
emojiDrawerFragment.setComposeEditText(composeText);
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.emoji_drawer, emojiDrawerFragment)
|
||||||
|
.commit();
|
||||||
|
getSupportFragmentManager().executePendingTransactions();
|
||||||
|
emojiDrawer = Optional.of(emojiDrawerFragment);
|
||||||
|
return emojiDrawerFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmojiDrawerOpen() {
|
||||||
|
return emojiDrawer.isPresent() && emojiDrawer.get().isOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
@ -1094,16 +1097,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
|
||||||
if (emojiDrawer.isPresent() && emojiDrawer.get().isOpen()) {
|
if (isEmojiDrawerOpen()) {
|
||||||
input.showSoftInput(composeText, 0);
|
input.showSoftInput(composeText, 0);
|
||||||
emojiDrawer.get().hide();
|
getEmojiDrawer().hide();
|
||||||
} else {
|
} else {
|
||||||
if (!emojiDrawer.isPresent()) {
|
|
||||||
emojiDrawer = Optional.of(initializeEmojiDrawer());
|
|
||||||
}
|
|
||||||
input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
|
input.hideSoftInputFromWindow(composeText.getWindowToken(), 0);
|
||||||
|
|
||||||
emojiDrawer.get().show();
|
getEmojiDrawer().show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1141,7 +1141,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (emojiDrawer.isPresent() && emojiDrawer.get().isOpen()) {
|
if (isEmojiDrawerOpen()) {
|
||||||
emojiToggle.performClick();
|
emojiToggle.performClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1157,7 +1157,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
if (hasFocus && emojiDrawer.isPresent() && emojiDrawer.get().isOpen()) {
|
if (hasFocus && isEmojiDrawerOpen()) {
|
||||||
emojiToggle.performClick();
|
emojiToggle.performClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,6 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
|
|||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -239,9 +238,7 @@ public class ConversationItem extends LinearLayout {
|
|||||||
if (isCaptionlessMms(messageRecord)) {
|
if (isCaptionlessMms(messageRecord)) {
|
||||||
bodyText.setVisibility(View.GONE);
|
bodyText.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(),
|
bodyText.setText(messageRecord.getDisplayBody());
|
||||||
new Emoji.InvalidatingPageLoadedListener(bodyText)),
|
|
||||||
TextView.BufferType.SPANNABLE);
|
|
||||||
bodyText.setVisibility(View.VISIBLE);
|
bodyText.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
|||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -96,10 +95,7 @@ public class ConversationListItem extends RelativeLayout
|
|||||||
this.recipients.addListener(this);
|
this.recipients.addListener(this);
|
||||||
this.fromView.setText(recipients, read);
|
this.fromView.setText(recipients, read);
|
||||||
|
|
||||||
this.subjectView.setText(Emoji.getInstance(context).emojify(thread.getDisplayBody(),
|
this.subjectView.setText(thread.getDisplayBody());
|
||||||
Emoji.EMOJI_SMALL,
|
|
||||||
new Emoji.InvalidatingPageLoadedListener(subjectView)),
|
|
||||||
TextView.BufferType.SPANNABLE);
|
|
||||||
this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
this.subjectView.setTypeface(read ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
||||||
|
|
||||||
if (thread.getDate() > 0) {
|
if (thread.getDate() > 0) {
|
||||||
|
@ -9,7 +9,7 @@ import android.text.TextUtils;
|
|||||||
import android.text.style.RelativeSizeSpan;
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
public class ComposeText extends AppCompatEditText {
|
public class ComposeText extends EmojiEditText {
|
||||||
public ComposeText(Context context) {
|
public ComposeText(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
@ -1,273 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.support.v4.view.PagerAdapter;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AbsListView;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.GridView;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
|
|
||||||
import com.astuetz.PagerSlidingTabStrip;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener;
|
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
|
||||||
|
|
||||||
public class EmojiDrawer extends KeyboardAwareLinearLayout {
|
|
||||||
private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
|
||||||
|
|
||||||
private static final int RECENT_TYPE = 0;
|
|
||||||
private static final int ALL_TYPE = 1;
|
|
||||||
|
|
||||||
|
|
||||||
private FrameLayout[] gridLayouts = new FrameLayout[Emoji.PAGES.length+1];
|
|
||||||
private EditText composeText;
|
|
||||||
private Emoji emoji;
|
|
||||||
private ViewPager pager;
|
|
||||||
private PagerSlidingTabStrip strip;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public EmojiDrawer(Context context) {
|
|
||||||
super(context);
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public EmojiDrawer(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
|
||||||
public EmojiDrawer(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComposeEditText(EditText composeText) {
|
|
||||||
this.composeText = composeText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOpen() {
|
|
||||||
return getVisibility() == View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize() {
|
|
||||||
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
inflater.inflate(R.layout.emoji_drawer, this, true);
|
|
||||||
|
|
||||||
initializeResources();
|
|
||||||
|
|
||||||
if (!this.isInEditMode()) {
|
|
||||||
initializeEmojiGrid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeResources() {
|
|
||||||
this.pager = (ViewPager) findViewById(R.id.emoji_pager);
|
|
||||||
this.strip = (PagerSlidingTabStrip) findViewById(R.id.tabs);
|
|
||||||
this.emoji = Emoji.getInstance(getContext());
|
|
||||||
|
|
||||||
RepeatableImageKey backspace = (RepeatableImageKey)findViewById(R.id.backspace);
|
|
||||||
backspace.setOnKeyEventListener(new KeyEventListener() {
|
|
||||||
@Override public void onKeyEvent() {
|
|
||||||
if (composeText != null && composeText.getText().length() > 0) {
|
|
||||||
composeText.dispatchKeyEvent(DELETE_KEY_EVENT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hide() {
|
|
||||||
setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
|
||||||
int keyboardHeight = getKeyboardHeight();
|
|
||||||
Log.w("EmojiDrawer", "setting emoji drawer to height " + keyboardHeight);
|
|
||||||
setLayoutParams(new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, keyboardHeight));
|
|
||||||
requestLayout();
|
|
||||||
setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeEmojiGrid() {
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
|
||||||
for (int i = 0; i < gridLayouts.length; i++) {
|
|
||||||
gridLayouts[i] = (FrameLayout) inflater.inflate(R.layout.emoji_grid_layout, pager, false);
|
|
||||||
final GridView gridView = (GridView) gridLayouts[i].findViewById(R.id.emoji);
|
|
||||||
gridLayouts[i].setTag(gridView);
|
|
||||||
final int type = (i == 0 ? RECENT_TYPE : ALL_TYPE);
|
|
||||||
gridView.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2*getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding));
|
|
||||||
gridView.setAdapter(new EmojiGridAdapter(type, i-1));
|
|
||||||
gridView.setOnItemClickListener(new EmojiClickListener(ALL_TYPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
pager.setAdapter(new EmojiPagerAdapter());
|
|
||||||
|
|
||||||
if (emoji.getRecentlyUsedAssetCount() <= 0) {
|
|
||||||
pager.setCurrentItem(1);
|
|
||||||
}
|
|
||||||
strip.setTabPaddingLeftRight(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_left_right_padding));
|
|
||||||
strip.setAllCaps(false);
|
|
||||||
strip.setShouldExpand(true);
|
|
||||||
strip.setUnderlineColorResource(R.color.emoji_tab_underline);
|
|
||||||
strip.setIndicatorColorResource(R.color.emoji_tab_indicator);
|
|
||||||
strip.setIndicatorHeight(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_indicator_height));
|
|
||||||
|
|
||||||
strip.setViewPager(pager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EmojiClickListener implements AdapterView.OnItemClickListener {
|
|
||||||
|
|
||||||
private final int type;
|
|
||||||
|
|
||||||
public EmojiClickListener(int type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
Integer unicodePoint = (Integer) view.getTag();
|
|
||||||
insertEmoji(composeText, unicodePoint);
|
|
||||||
if (type != RECENT_TYPE) {
|
|
||||||
emoji.setRecentlyUsed(Integer.toHexString(unicodePoint));
|
|
||||||
((BaseAdapter)((GridView)gridLayouts[0].getTag()).getAdapter()).notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void insertEmoji(EditText editText, Integer unicodePoint) {
|
|
||||||
final char[] chars = Character.toChars(unicodePoint);
|
|
||||||
String characters = new String(chars);
|
|
||||||
int start = editText.getSelectionStart();
|
|
||||||
int end = editText.getSelectionEnd();
|
|
||||||
|
|
||||||
CharSequence text = emoji.emojify(characters, new Emoji.InvalidatingPageLoadedListener(composeText));
|
|
||||||
editText.getText().replace(Math.min(start, end), Math.max(start, end), text, 0, text.length());
|
|
||||||
|
|
||||||
editText.setSelection(end+chars.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private class EmojiGridAdapter extends BaseAdapter {
|
|
||||||
|
|
||||||
private final int type;
|
|
||||||
private final int page;
|
|
||||||
private final int emojiSize;
|
|
||||||
|
|
||||||
public EmojiGridAdapter(int type, int page) {
|
|
||||||
this.type = type;
|
|
||||||
this.page = page;
|
|
||||||
emojiSize = (int) getResources().getDimension(R.dimen.emoji_drawer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
if (type == RECENT_TYPE) return emoji.getRecentlyUsedAssetCount();
|
|
||||||
else return Emoji.PAGES[page].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
|
||||||
final ImageView view;
|
|
||||||
final int pad = getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
|
||||||
if (convertView != null && convertView instanceof ImageView) {
|
|
||||||
view = (ImageView) convertView;
|
|
||||||
} else {
|
|
||||||
ImageView imageView = new ImageView(getContext());
|
|
||||||
imageView.setPadding(pad, pad, pad, pad);
|
|
||||||
imageView.setLayoutParams(new AbsListView.LayoutParams(emojiSize + 2*pad, emojiSize + 2*pad));
|
|
||||||
view = imageView;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Drawable drawable;
|
|
||||||
final Integer unicodeTag;
|
|
||||||
if (type == ALL_TYPE) {
|
|
||||||
unicodeTag = Emoji.PAGES[page][position];
|
|
||||||
drawable = emoji.getEmojiDrawable(new Emoji.DrawInfo(page, position),
|
|
||||||
Emoji.EMOJI_HUGE,
|
|
||||||
new Emoji.InvalidatingPageLoadedListener(view));
|
|
||||||
} else {
|
|
||||||
Pair<Integer, Drawable> recentlyUsed = emoji.getRecentlyUsed(position,
|
|
||||||
Emoji.EMOJI_HUGE,
|
|
||||||
new Emoji.InvalidatingPageLoadedListener(view));
|
|
||||||
unicodeTag = recentlyUsed.first;
|
|
||||||
drawable = recentlyUsed.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
view.setImageDrawable(drawable);
|
|
||||||
view.setPadding(pad, pad, pad, pad);
|
|
||||||
view.setTag(unicodeTag);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EmojiPagerAdapter extends PagerAdapter implements PagerSlidingTabStrip.IconTabProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return gridLayouts.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isViewFromObject(View view, Object o) {
|
|
||||||
return view == o;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object instantiateItem(ViewGroup container, int position) {
|
|
||||||
if (position < 0 || position >= gridLayouts.length)
|
|
||||||
throw new AssertionError("position out of range!");
|
|
||||||
|
|
||||||
container.addView(gridLayouts[position], 0);
|
|
||||||
|
|
||||||
return gridLayouts[position];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
|
||||||
Log.w("EmojiDrawer", "destroying item at " + position);
|
|
||||||
container.removeView(gridLayouts[position]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPageIconResId(int i) {
|
|
||||||
switch (i) {
|
|
||||||
case 0: return R.drawable.emoji_category_recent;
|
|
||||||
case 1: return R.drawable.emoji_category_smile;
|
|
||||||
case 2: return R.drawable.emoji_category_flower;
|
|
||||||
case 3: return R.drawable.emoji_category_bell;
|
|
||||||
case 4: return R.drawable.emoji_category_car;
|
|
||||||
case 5: return R.drawable.emoji_category_symbol;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
156
src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
Normal file
156
src/org/thoughtcrime/securesms/components/emoji/EmojiDrawer.java
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.ArrayRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
import com.astuetz.PagerSlidingTabStrip;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||||
|
import org.thoughtcrime.securesms.components.RepeatableImageKey;
|
||||||
|
import org.thoughtcrime.securesms.components.RepeatableImageKey.KeyEventListener;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiPageFragment.EmojiSelectionListener;
|
||||||
|
import org.thoughtcrime.securesms.util.ResUtil;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EmojiDrawer extends Fragment {
|
||||||
|
private static final KeyEvent DELETE_KEY_EVENT = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
||||||
|
|
||||||
|
private EmojiEditText composeText;
|
||||||
|
private KeyboardAwareLinearLayout container;
|
||||||
|
private ViewPager pager;
|
||||||
|
private PagerSlidingTabStrip strip;
|
||||||
|
|
||||||
|
public static EmojiDrawer newInstance(@ArrayRes int categories, @ArrayRes int icons) {
|
||||||
|
final EmojiDrawer fragment = new EmojiDrawer();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putInt("categories", categories);
|
||||||
|
args.putInt("icons", icons);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmojiDrawer newInstance() {
|
||||||
|
return newInstance(R.array.emoji_categories, R.array.emoji_category_icons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComposeEditText(EmojiEditText composeText) {
|
||||||
|
this.composeText = composeText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
final View v = inflater.inflate(R.layout.emoji_drawer, container, false);
|
||||||
|
initializeResources(v);
|
||||||
|
initializeEmojiGrid();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeResources(View v) {
|
||||||
|
Log.w("EmojiDrawer", "initializeResources()");
|
||||||
|
this.container = (KeyboardAwareLinearLayout) v.findViewById(R.id.container);
|
||||||
|
this.pager = (ViewPager) v.findViewById(R.id.emoji_pager);
|
||||||
|
this.strip = (PagerSlidingTabStrip) v.findViewById(R.id.tabs);
|
||||||
|
|
||||||
|
RepeatableImageKey backspace = (RepeatableImageKey)v.findViewById(R.id.backspace);
|
||||||
|
backspace.setOnKeyEventListener(new KeyEventListener() {
|
||||||
|
@Override public void onKeyEvent() {
|
||||||
|
if (composeText != null && composeText.getText().length() > 0) {
|
||||||
|
composeText.dispatchKeyEvent(DELETE_KEY_EVENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide() {
|
||||||
|
container.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
int keyboardHeight = container.getKeyboardHeight();
|
||||||
|
Log.w("EmojiDrawer", "setting emoji drawer to height " + keyboardHeight);
|
||||||
|
container.setLayoutParams(new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, keyboardHeight));
|
||||||
|
container.requestLayout();
|
||||||
|
container.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpen() {
|
||||||
|
return container.getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeEmojiGrid() {
|
||||||
|
|
||||||
|
pager.setAdapter(new EmojiPagerAdapter(getActivity(),
|
||||||
|
getFragmentManager(),
|
||||||
|
getPageModels(getArguments().getInt("categories"),
|
||||||
|
getArguments().getInt("icons")),
|
||||||
|
new EmojiSelectionListener() {
|
||||||
|
@Override public void onEmojiSelected(int emojiCode) {
|
||||||
|
composeText.insertEmoji(emojiCode);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
strip.setViewPager(pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<EmojiPageModel> getPageModels(@ArrayRes int pagesRes, @ArrayRes int iconsRes) {
|
||||||
|
final int[] icons = ResUtil.getResourceIds(getActivity(), iconsRes);
|
||||||
|
final int[] pages = ResUtil.getResourceIds(getActivity(), pagesRes);
|
||||||
|
final List<EmojiPageModel> models = new LinkedList<>();
|
||||||
|
models.add(new RecentEmojiPageModel(getActivity()));
|
||||||
|
for (int i = 0; i < icons.length; i++) {
|
||||||
|
models.add(new StaticEmojiPageModel(icons[i], getResources().getIntArray(pages[i])));
|
||||||
|
}
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EmojiPagerAdapter extends FragmentStatePagerAdapter
|
||||||
|
implements PagerSlidingTabStrip.CustomTabProvider
|
||||||
|
{
|
||||||
|
private Context context;
|
||||||
|
private List<EmojiPageModel> pages;
|
||||||
|
private EmojiSelectionListener listener;
|
||||||
|
|
||||||
|
public EmojiPagerAdapter(@NonNull Context context,
|
||||||
|
@NonNull FragmentManager fm,
|
||||||
|
@NonNull List<EmojiPageModel> pages,
|
||||||
|
@Nullable EmojiSelectionListener listener)
|
||||||
|
{
|
||||||
|
super(fm);
|
||||||
|
this.context = context;
|
||||||
|
this.pages = pages;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return pages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public Fragment getItem(int i) {
|
||||||
|
return EmojiPageFragment.newInstance(pages.get(i), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public View getCustomTabView(ViewGroup viewGroup, int i) {
|
||||||
|
ImageView image = new ImageView(context);
|
||||||
|
image.setImageResource(pages.get(i).getIconRes());
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v7.widget.AppCompatEditText;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
public class EmojiEditText extends AppCompatEditText {
|
||||||
|
public EmojiEditText(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiEditText(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiEditText(Context context, AttributeSet attrs,
|
||||||
|
int defStyleAttr)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
setTransformationMethod(new EmojiTransformationMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertEmoji(int codePoint) {
|
||||||
|
final char[] chars = Character.toChars(codePoint);
|
||||||
|
final String text = new String(chars);
|
||||||
|
final int start = getSelectionStart();
|
||||||
|
final int end = getSelectionEnd();
|
||||||
|
|
||||||
|
getText().replace(Math.min(start, end), Math.max(start, end), text, 0, text.length());
|
||||||
|
setSelection(end + chars.length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AbsListView;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.GridView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.InvalidatingPageLoadedListener;
|
||||||
|
|
||||||
|
public class EmojiPageFragment extends Fragment {
|
||||||
|
private static final String TAG = EmojiPageFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
private EmojiPageModel model;
|
||||||
|
private EmojiSelectionListener listener;
|
||||||
|
|
||||||
|
public static EmojiPageFragment newInstance(@NonNull EmojiPageModel model,
|
||||||
|
@Nullable EmojiSelectionListener listener)
|
||||||
|
{
|
||||||
|
EmojiPageFragment fragment = new EmojiPageFragment();
|
||||||
|
fragment.setModel(model);
|
||||||
|
fragment.setEmojiSelectedListener(listener);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
final View view = inflater.inflate(R.layout.emoji_grid_layout, container, false);
|
||||||
|
final GridView grid = (GridView) view.findViewById(R.id.emoji);
|
||||||
|
grid.setColumnWidth(getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size) + 2 * getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding));
|
||||||
|
grid.setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
model.onCodePointSelected((Integer)view.getTag());
|
||||||
|
if (listener != null) listener.onEmojiSelected((Integer)view.getTag());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
grid.setAdapter(new EmojiGridAdapter(getActivity(), model));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(EmojiPageModel model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmojiSelectedListener(EmojiSelectionListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EmojiGridAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
protected final Context context;
|
||||||
|
private final int emojiSize;
|
||||||
|
private final EmojiPageModel model;
|
||||||
|
|
||||||
|
public EmojiGridAdapter(Context context, EmojiPageModel model) {
|
||||||
|
this.context = context;
|
||||||
|
this.emojiSize = (int) context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int getCount() {
|
||||||
|
return model.getCodePoints().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||||
|
final ImageView view;
|
||||||
|
final int pad = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_item_padding);
|
||||||
|
if (convertView != null && convertView instanceof ImageView) {
|
||||||
|
view = (ImageView)convertView;
|
||||||
|
} else {
|
||||||
|
ImageView imageView = new ImageView(context);
|
||||||
|
imageView.setPadding(pad, pad, pad, pad);
|
||||||
|
imageView.setLayoutParams(new AbsListView.LayoutParams(emojiSize + 2 * pad, emojiSize + 2 * pad));
|
||||||
|
view = imageView;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Integer unicodeTag = model.getCodePoints()[position];
|
||||||
|
final EmojiProvider provider = EmojiProvider.getInstance(context);
|
||||||
|
final Drawable drawable = provider.getEmojiDrawable(unicodeTag,
|
||||||
|
EmojiProvider.EMOJI_HUGE,
|
||||||
|
new InvalidatingPageLoadedListener(view));
|
||||||
|
|
||||||
|
view.setImageDrawable(drawable);
|
||||||
|
view.setPadding(pad, pad, pad, pad);
|
||||||
|
view.setTag(unicodeTag);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EmojiSelectionListener {
|
||||||
|
void onEmojiSelected(int emojiCode);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
public interface EmojiPageModel {
|
||||||
|
int getIconRes();
|
||||||
|
int[] getCodePoints();
|
||||||
|
void onCodePointSelected(int codePoint);
|
||||||
|
}
|
@ -0,0 +1,257 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.style.ImageSpan;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.ResUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class EmojiProvider {
|
||||||
|
private static final String TAG = EmojiProvider.class.getSimpleName();
|
||||||
|
private static final ExecutorService executor = Util.newSingleThreadedLifoExecutor();
|
||||||
|
private static volatile EmojiProvider instance = null;
|
||||||
|
private static final SparseArray<SoftReference<Bitmap>> bitmaps = new SparseArray<>();
|
||||||
|
|
||||||
|
private final SparseArray<DrawInfo> offsets = new SparseArray<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("MalformedRegex")
|
||||||
|
// 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
|
||||||
|
// |==== misc ====||======== emoticons ========||========= flags ==========|
|
||||||
|
private static final Pattern EMOJI_RANGE = Pattern.compile("[\\u20a0-\\u32ff\\ud83c\\udc00-\\ud83d\\udeff\\udbb9\\udce5-\\udbb9\\udcee]");
|
||||||
|
|
||||||
|
public static final double EMOJI_HUGE = 1.00;
|
||||||
|
public static final double EMOJI_LARGE = 0.75;
|
||||||
|
public static final double EMOJI_SMALL = 0.60;
|
||||||
|
public static final int EMOJI_RAW_SIZE = 128;
|
||||||
|
public static final int EMOJI_PER_ROW = 16;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final int bigDrawSize;
|
||||||
|
private final int[] pages;
|
||||||
|
|
||||||
|
public static EmojiProvider getInstance(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (EmojiProvider.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new EmojiProvider(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmojiProvider(Context context) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
this.bigDrawSize = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size);
|
||||||
|
this.pages = ResUtil.getResourceIds(context, R.array.emoji_categories);
|
||||||
|
for (int i = 0; i < pages.length; i++) {
|
||||||
|
final int[] page = context.getResources().getIntArray(pages[i]);
|
||||||
|
for (int j = 0; j < page.length; j++) {
|
||||||
|
offsets.put(page[j], new DrawInfo(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preloadPage(final int page, final PageLoadedListener pageLoadListener) {
|
||||||
|
executor.submit(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
loadPage(page);
|
||||||
|
if (pageLoadListener != null) {
|
||||||
|
Log.w(TAG, "onPageLoaded("+page+")");
|
||||||
|
pageLoadListener.onPageLoaded();
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPage(int page) throws IOException {
|
||||||
|
if (page < 0 || page >= pages.length) {
|
||||||
|
throw new IndexOutOfBoundsException("can't load page that doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitmaps.get(page) != null && bitmaps.get(page).get() != null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String file = "emoji_" + page + "_wrapped.png";
|
||||||
|
final InputStream measureStream = context.getAssets().open(file);
|
||||||
|
final InputStream bitmapStream = context.getAssets().open(file);
|
||||||
|
final Bitmap bitmap = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float) bigDrawSize / (float) EMOJI_RAW_SIZE);
|
||||||
|
bitmaps.put(page, new SoftReference<>(bitmap));
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, ioe);
|
||||||
|
throw ioe;
|
||||||
|
} catch (BitmapDecodingException bde) {
|
||||||
|
Log.w(TAG, bde);
|
||||||
|
throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence emojify(CharSequence text, PageLoadedListener pageLoadedListener) {
|
||||||
|
return emojify(text, EMOJI_LARGE, pageLoadedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence emojify(CharSequence text, double size, PageLoadedListener pageLoadedListener) {
|
||||||
|
Matcher matches = EMOJI_RANGE.matcher(text);
|
||||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
||||||
|
|
||||||
|
while (matches.find()) {
|
||||||
|
int codePoint = matches.group().codePointAt(0);
|
||||||
|
Drawable drawable = getEmojiDrawable(codePoint, size, pageLoadedListener);
|
||||||
|
if (drawable != null) {
|
||||||
|
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
|
||||||
|
char[] chars = new char[matches.end() - matches.start()];
|
||||||
|
Arrays.fill(chars, ' ');
|
||||||
|
builder.setSpan(imageSpan, matches.start(), matches.end(),
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable getEmojiDrawable(int emojiCode, double size, PageLoadedListener pageLoadedListener) {
|
||||||
|
return getEmojiDrawable(offsets.get(emojiCode), size, pageLoadedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getEmojiDrawable(DrawInfo drawInfo, double size, PageLoadedListener pageLoadedListener) {
|
||||||
|
if (drawInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Drawable drawable = new EmojiDrawable(drawInfo, bigDrawSize);
|
||||||
|
drawable.setBounds(0, 0, (int)((double)bigDrawSize * size), (int)((double)bigDrawSize * size));
|
||||||
|
if (bitmaps.get(drawInfo.page) == null || bitmaps.get(drawInfo.page).get() == null) {
|
||||||
|
preloadPage(drawInfo.page, pageLoadedListener);
|
||||||
|
}
|
||||||
|
return drawable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EmojiDrawable extends Drawable {
|
||||||
|
private final int index;
|
||||||
|
private final int page;
|
||||||
|
private final int emojiSize;
|
||||||
|
private static final Paint paint;
|
||||||
|
private Bitmap bmp;
|
||||||
|
|
||||||
|
static {
|
||||||
|
paint = new Paint();
|
||||||
|
paint.setFilterBitmap(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiDrawable(DrawInfo info, int emojiSize) {
|
||||||
|
this.index = info.index;
|
||||||
|
this.page = info.page;
|
||||||
|
this.emojiSize = emojiSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
if (bitmaps.get(page) == null || bitmaps.get(page).get() == null) {
|
||||||
|
Log.w(TAG, "bitmap for this page was null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bmp == null) {
|
||||||
|
bmp = bitmaps.get(page).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect b = copyBounds();
|
||||||
|
|
||||||
|
final int row = index / EMOJI_PER_ROW;
|
||||||
|
final int row_index = index % EMOJI_PER_ROW;
|
||||||
|
|
||||||
|
canvas.drawBitmap(bmp,
|
||||||
|
new Rect(row_index * emojiSize,
|
||||||
|
row * emojiSize,
|
||||||
|
(row_index + 1) * emojiSize,
|
||||||
|
(row + 1) * emojiSize),
|
||||||
|
b,
|
||||||
|
paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.TRANSLUCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter cf) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EmojiDrawable{" +
|
||||||
|
"page=" + page +
|
||||||
|
", index=" + index +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvalidatingPageLoadedListener implements PageLoadedListener {
|
||||||
|
private final View view;
|
||||||
|
|
||||||
|
public InvalidatingPageLoadedListener(final View view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageLoaded() {
|
||||||
|
view.postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "InvalidatingPageLoadedListener{}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DrawInfo {
|
||||||
|
int page;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
public DrawInfo(final int page, final int index) {
|
||||||
|
this.page = page;
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DrawInfo{" +
|
||||||
|
"page=" + page +
|
||||||
|
", index=" + index +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PageLoadedListener {
|
||||||
|
void onPageLoaded();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build.VERSION_CODES;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public class EmojiTextView extends TextView {
|
||||||
|
public EmojiTextView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiTextView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||||
|
public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
setTransformationMethod(new EmojiTransformationMethod());
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.text.method.TransformationMethod;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.InvalidatingPageLoadedListener;
|
||||||
|
|
||||||
|
class EmojiTransformationMethod implements TransformationMethod {
|
||||||
|
|
||||||
|
@Override public CharSequence getTransformation(CharSequence source, View view) {
|
||||||
|
return EmojiProvider.getInstance(view.getContext()).emojify(source, EmojiProvider.EMOJI_SMALL, new InvalidatingPageLoadedListener(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onFocusChanged(View view, CharSequence sourceText, boolean focused,
|
||||||
|
int direction, Rect previouslyFocusedRect) { }
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||||
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
|
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||||
|
private static final String TAG = RecentEmojiPageModel.class.getSimpleName();
|
||||||
|
private static final String EMOJI_LRU_PREFERENCE = "pref_recent_emoji";
|
||||||
|
private static final int EMOJI_LRU_SIZE = 50;
|
||||||
|
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
private final LinkedHashSet<Integer> recentlyUsed;
|
||||||
|
|
||||||
|
public RecentEmojiPageModel(Context context) {
|
||||||
|
this.prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
this.recentlyUsed = getPersistedCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedHashSet<Integer> getPersistedCache() {
|
||||||
|
String serialized = prefs.getString(EMOJI_LRU_PREFERENCE, "[]");
|
||||||
|
LinkedHashSet<String> recentlyUsedStrings;
|
||||||
|
try {
|
||||||
|
CollectionType collectionType = TypeFactory.defaultInstance()
|
||||||
|
.constructCollectionType(LinkedHashSet.class, String.class);
|
||||||
|
recentlyUsedStrings = JsonUtils.getMapper().readValue(serialized, collectionType);
|
||||||
|
return fromHexString(recentlyUsedStrings);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return new LinkedHashSet<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int getIconRes() {
|
||||||
|
return R.drawable.emoji_category_recent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int[] getCodePoints() {
|
||||||
|
return toPrimitiveArray(recentlyUsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onCodePointSelected(int codePoint) {
|
||||||
|
recentlyUsed.remove(codePoint);
|
||||||
|
recentlyUsed.add(codePoint);
|
||||||
|
|
||||||
|
if (recentlyUsed.size() > EMOJI_LRU_SIZE) {
|
||||||
|
Iterator<Integer> iterator = recentlyUsed.iterator();
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
final LinkedHashSet<Integer> latestRecentlyUsed = new LinkedHashSet<>(recentlyUsed);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
String serialized = JsonUtils.toJson(toHexString(latestRecentlyUsed));
|
||||||
|
prefs.edit()
|
||||||
|
.putString(EMOJI_LRU_PREFERENCE, serialized)
|
||||||
|
.apply();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedHashSet<Integer> fromHexString(@Nullable LinkedHashSet<String> stringSet) {
|
||||||
|
final LinkedHashSet<Integer> integerSet = new LinkedHashSet<>(stringSet != null ? stringSet.size() : 0);
|
||||||
|
if (stringSet != null) {
|
||||||
|
for (String hexString : stringSet) {
|
||||||
|
integerSet.add(Integer.valueOf(hexString, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return integerSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedHashSet<String> toHexString(@NonNull LinkedHashSet<Integer> integerSet) {
|
||||||
|
final LinkedHashSet<String> stringSet = new LinkedHashSet<>(integerSet.size());
|
||||||
|
for (Integer integer : integerSet) {
|
||||||
|
stringSet.add(Integer.toHexString(integer));
|
||||||
|
}
|
||||||
|
return stringSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] toPrimitiveArray(@NonNull LinkedHashSet<Integer> integerSet) {
|
||||||
|
int[] ints = new int[integerSet.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Integer integer : integerSet) {
|
||||||
|
ints[i++] = integer;
|
||||||
|
}
|
||||||
|
return ints;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
|
import android.support.annotation.DrawableRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
public class StaticEmojiPageModel implements EmojiPageModel {
|
||||||
|
@DrawableRes private final int icon;
|
||||||
|
@NonNull private final int[] codePoints;
|
||||||
|
|
||||||
|
public StaticEmojiPageModel(@DrawableRes int icon, @NonNull int[] codePoints) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.codePoints = codePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIconRes() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull public int[] getCodePoints() {
|
||||||
|
return codePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onCodePointSelected(int codePoint) { }
|
||||||
|
}
|
@ -1,474 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.ColorFilter;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.text.Spannable;
|
|
||||||
import android.text.SpannableString;
|
|
||||||
import android.text.style.ImageSpan;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class Emoji {
|
|
||||||
|
|
||||||
private static final String TAG = Emoji.class.getSimpleName();
|
|
||||||
|
|
||||||
private static ExecutorService executor = Util.newSingleThreadedLifoExecutor();
|
|
||||||
|
|
||||||
public static final int[][] PAGES = {
|
|
||||||
{
|
|
||||||
0x263a, 0x1f60a, 0x1f600, 0x1f601, 0x1f602, 0x1f603, 0x1f604, 0x1f605,
|
|
||||||
0x1f606, 0x1f607, 0x1f608, 0x1f609, 0x1f62f, 0x1f610, 0x1f611, 0x1f615,
|
|
||||||
0x1f620, 0x1f62c, 0x1f621, 0x1f622, 0x1f634, 0x1f62e, 0x1f623, 0x1f624,
|
|
||||||
0x1f625, 0x1f626, 0x1f627, 0x1f628, 0x1f629, 0x1f630, 0x1f61f, 0x1f631,
|
|
||||||
0x1f632, 0x1f633, 0x1f635, 0x1f636, 0x1f637, 0x1f61e, 0x1f612, 0x1f60d,
|
|
||||||
0x1f61b, 0x1f61c, 0x1f61d, 0x1f60b, 0x1f617, 0x1f619, 0x1f618, 0x1f61a,
|
|
||||||
0x1f60e, 0x1f62d, 0x1f60c, 0x1f616, 0x1f614, 0x1f62a, 0x1f60f, 0x1f613,
|
|
||||||
0x1f62b, 0x1f64b, 0x1f64c, 0x1f64d, 0x1f645, 0x1f646, 0x1f647, 0x1f64e,
|
|
||||||
0x1f64f, 0x1f63a, 0x1f63c, 0x1f638, 0x1f639, 0x1f63b, 0x1f63d, 0x1f63f,
|
|
||||||
0x1f63e, 0x1f640, 0x1f648, 0x1f649, 0x1f64a, 0x1f4a9, 0x1f476, 0x1f466,
|
|
||||||
0x1f467, 0x1f468, 0x1f469, 0x1f474, 0x1f475, 0x1f48f, 0x1f491, 0x1f46a,
|
|
||||||
0x1f46b, 0x1f46c, 0x1f46d, 0x1f464, 0x1f465, 0x1f46e, 0x1f477, 0x1f481,
|
|
||||||
0x1f482, 0x1f46f, 0x1f470, 0x1f478, 0x1f385, 0x1f47c, 0x1f471, 0x1f472,
|
|
||||||
0x1f473, 0x1f483, 0x1f486, 0x1f487, 0x1f485, 0x1f47b, 0x1f479, 0x1f47a,
|
|
||||||
0x1f47d, 0x1f47e, 0x1f47f, 0x1f480, 0x1f4aa, 0x1f440, 0x1f442, 0x1f443,
|
|
||||||
0x1f463, 0x1f444, 0x1f445, 0x1f48b, 0x2764, 0x1f499, 0x1f49a, 0x1f49b,
|
|
||||||
0x1f49c, 0x1f493, 0x1f494, 0x1f495, 0x1f496, 0x1f497, 0x1f498, 0x1f49d,
|
|
||||||
0x1f49e, 0x1f49f, 0x1f44d, 0x1f44e, 0x1f44c, 0x270a, 0x270c, 0x270b,
|
|
||||||
0x1f44a, 0x261d, 0x1f446, 0x1f447, 0x1f448, 0x1f449, 0x1f44b, 0x1f44f,
|
|
||||||
0x1f450
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x1f530, 0x1f484, 0x1f45e, 0x1f45f, 0x1f451, 0x1f452, 0x1f3a9, 0x1f393,
|
|
||||||
0x1f453, 0x231a, 0x1f454, 0x1f455, 0x1f456, 0x1f457, 0x1f458, 0x1f459,
|
|
||||||
0x1f460, 0x1f461, 0x1f462, 0x1f45a, 0x1f45c, 0x1f4bc, 0x1f392, 0x1f45d,
|
|
||||||
0x1f45b, 0x1f4b0, 0x1f4b3, 0x1f4b2, 0x1f4b5, 0x1f4b4, 0x1f4b6, 0x1f4b7,
|
|
||||||
0x1f4b8, 0x1f4b1, 0x1f4b9, 0x1f52b, 0x1f52a, 0x1f4a3, 0x1f489, 0x1f48a,
|
|
||||||
0x1f6ac, 0x1f514, 0x1f515, 0x1f6aa, 0x1f52c, 0x1f52d, 0x1f52e, 0x1f526,
|
|
||||||
0x1f50b, 0x1f50c, 0x1f4dc, 0x1f4d7, 0x1f4d8, 0x1f4d9, 0x1f4da, 0x1f4d4,
|
|
||||||
0x1f4d2, 0x1f4d1, 0x1f4d3, 0x1f4d5, 0x1f4d6, 0x1f4f0, 0x1f4db, 0x1f383,
|
|
||||||
0x1f384, 0x1f380, 0x1f381, 0x1f382, 0x1f388, 0x1f386, 0x1f387, 0x1f389,
|
|
||||||
0x1f38a, 0x1f38d, 0x1f38f, 0x1f38c, 0x1f390, 0x1f38b, 0x1f38e, 0x1f4f1,
|
|
||||||
0x1f4f2, 0x1f4df, 0x260e, 0x1f4de, 0x1f4e0, 0x1f4e6, 0x2709, 0x1f4e8,
|
|
||||||
0x1f4e9, 0x1f4ea, 0x1f4eb, 0x1f4ed, 0x1f4ec, 0x1f4ee, 0x1f4e4, 0x1f4e5,
|
|
||||||
0x1f4ef, 0x1f4e2, 0x1f4e3, 0x1f4e1, 0x1f4ac, 0x1f4ad, 0x2712, 0x270f,
|
|
||||||
0x1f4dd, 0x1f4cf, 0x1f4d0, 0x1f4cd, 0x1f4cc, 0x1f4ce, 0x2702, 0x1f4ba,
|
|
||||||
0x1f4bb, 0x1f4bd, 0x1f4be, 0x1f4bf, 0x1f4c6, 0x1f4c5, 0x1f4c7, 0x1f4cb,
|
|
||||||
0x1f4c1, 0x1f4c2, 0x1f4c3, 0x1f4c4, 0x1f4ca, 0x1f4c8, 0x1f4c9, 0x26fa,
|
|
||||||
0x1f3a1, 0x1f3a2, 0x1f3a0, 0x1f3aa, 0x1f3a8, 0x1f3ac, 0x1f3a5, 0x1f4f7,
|
|
||||||
0x1f4f9, 0x1f3a6, 0x1f3ad, 0x1f3ab, 0x1f3ae, 0x1f3b2, 0x1f3b0, 0x1f0cf,
|
|
||||||
0x1f3b4, 0x1f004, 0x1f3af, 0x1f4fa, 0x1f4fb, 0x1f4c0, 0x1f4fc, 0x1f3a7,
|
|
||||||
0x1f3a4, 0x1f3b5, 0x1f3b6, 0x1f3bc, 0x1f3bb, 0x1f3b9, 0x1f3b7, 0x1f3ba,
|
|
||||||
0x1f3b8, 0x303d
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x1f415, 0x1f436, 0x1f429, 0x1f408, 0x1f431, 0x1f400, 0x1f401, 0x1f42d,
|
|
||||||
0x1f439, 0x1f422, 0x1f407, 0x1f430, 0x1f413, 0x1f414, 0x1f423, 0x1f424,
|
|
||||||
0x1f425, 0x1f426, 0x1f40f, 0x1f411, 0x1f410, 0x1f43a, 0x1f403, 0x1f402,
|
|
||||||
0x1f404, 0x1f42e, 0x1f434, 0x1f417, 0x1f416, 0x1f437, 0x1f43d, 0x1f438,
|
|
||||||
0x1f40d, 0x1f43c, 0x1f427, 0x1f418, 0x1f428, 0x1f412, 0x1f435, 0x1f406,
|
|
||||||
0x1f42f, 0x1f43b, 0x1f42b, 0x1f42a, 0x1f40a, 0x1f433, 0x1f40b, 0x1f41f,
|
|
||||||
0x1f420, 0x1f421, 0x1f419, 0x1f41a, 0x1f42c, 0x1f40c, 0x1f41b, 0x1f41c,
|
|
||||||
0x1f41d, 0x1f41e, 0x1f432, 0x1f409, 0x1f43e, 0x1f378, 0x1f37a, 0x1f37b,
|
|
||||||
0x1f377, 0x1f379, 0x1f376, 0x2615, 0x1f375, 0x1f37c, 0x1f374, 0x1f368,
|
|
||||||
0x1f367, 0x1f366, 0x1f369, 0x1f370, 0x1f36a, 0x1f36b, 0x1f36c, 0x1f36d,
|
|
||||||
0x1f36e, 0x1f36f, 0x1f373, 0x1f354, 0x1f35f, 0x1f35d, 0x1f355, 0x1f356,
|
|
||||||
0x1f357, 0x1f364, 0x1f363, 0x1f371, 0x1f35e, 0x1f35c, 0x1f359, 0x1f35a,
|
|
||||||
0x1f35b, 0x1f372, 0x1f365, 0x1f362, 0x1f361, 0x1f358, 0x1f360, 0x1f34c,
|
|
||||||
0x1f34e, 0x1f34f, 0x1f34a, 0x1f34b, 0x1f344, 0x1f345, 0x1f346, 0x1f347,
|
|
||||||
0x1f348, 0x1f349, 0x1f350, 0x1f351, 0x1f352, 0x1f353, 0x1f34d, 0x1f330,
|
|
||||||
0x1f331, 0x1f332, 0x1f333, 0x1f334, 0x1f335, 0x1f337, 0x1f338, 0x1f339,
|
|
||||||
0x1f340, 0x1f341, 0x1f342, 0x1f343, 0x1f33a, 0x1f33b, 0x1f33c, 0x1f33d,
|
|
||||||
0x1f33e, 0x1f33f, 0x2600, 0x1f308, 0x26c5, 0x2601, 0x1f301, 0x1f302,
|
|
||||||
0x2614, 0x1f4a7, 0x26a1, 0x1f300, 0x2744, 0x26c4, 0x1f319, 0x1f31e,
|
|
||||||
0x1f31d, 0x1f31a, 0x1f31b, 0x1f31c, 0x1f311, 0x1f312, 0x1f313, 0x1f314,
|
|
||||||
0x1f315, 0x1f316, 0x1f317, 0x1f318, 0x1f391, 0x1f304, 0x1f305, 0x1f307,
|
|
||||||
0x1f306, 0x1f303, 0x1f30c, 0x1f309, 0x1f30a, 0x1f30b, 0x1f30e, 0x1f30f,
|
|
||||||
0x1f30d, 0x1f310
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x1f3e0, 0x1f3e1, 0x1f3e2, 0x1f3e3, 0x1f3e4, 0x1f3e5, 0x1f3e6, 0x1f3e7,
|
|
||||||
0x1f3e8, 0x1f3e9, 0x1f3ea, 0x1f3eb, 0x26ea, 0x26f2, 0x1f3ec, 0x1f3ef,
|
|
||||||
0x1f3f0, 0x1f3ed, 0x1f5fb, 0x1f5fc, 0x1f5fd, 0x1f5fe, 0x1f5ff, 0x2693,
|
|
||||||
0x1f3ee, 0x1f488, 0x1f527, 0x1f528, 0x1f529, 0x1f6bf, 0x1f6c1, 0x1f6c0,
|
|
||||||
0x1f6bd, 0x1f6be, 0x1f3bd, 0x1f3a3, 0x1f3b1, 0x1f3b3, 0x26be, 0x26f3,
|
|
||||||
0x1f3be, 0x26bd, 0x1f3bf, 0x1f3c0, 0x1f3c1, 0x1f3c2, 0x1f3c3, 0x1f3c4,
|
|
||||||
0x1f3c6, 0x1f3c7, 0x1f40e, 0x1f3c8, 0x1f3c9, 0x1f3ca, 0x1f682, 0x1f683,
|
|
||||||
0x1f684, 0x1f685, 0x1f686, 0x1f687, 0x24c2, 0x1f688, 0x1f68a, 0x1f68b,
|
|
||||||
0x1f68c, 0x1f68d, 0x1f68e, 0x1f68f, 0x1f690, 0x1f691, 0x1f692, 0x1f693,
|
|
||||||
0x1f694, 0x1f695, 0x1f696, 0x1f697, 0x1f698, 0x1f699, 0x1f69a, 0x1f69b,
|
|
||||||
0x1f69c, 0x1f69d, 0x1f69e, 0x1f69f, 0x1f6a0, 0x1f6a1, 0x1f6a2, 0x1f6a3,
|
|
||||||
0x1f681, 0x2708, 0x1f6c2, 0x1f6c3, 0x1f6c4, 0x1f6c5, 0x26f5, 0x1f6b2,
|
|
||||||
0x1f6b3, 0x1f6b4, 0x1f6b5, 0x1f6b7, 0x1f6b8, 0x1f689, 0x1f680, 0x1f6a4,
|
|
||||||
0x1f6b6, 0x26fd, 0x1f17f, 0x1f6a5, 0x1f6a6, 0x1f6a7, 0x1f6a8, 0x2668,
|
|
||||||
0x1f48c, 0x1f48d, 0x1f48e, 0x1f490, 0x1f492, 0xfe4e5, 0xfe4e6, 0xfe4e7,
|
|
||||||
0xfe4e8, 0xfe4e9, 0xfe4ea, 0xfe4eb, 0xfe4ec, 0xfe4ed, 0xfe4ee
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x1f51d, 0x1f519, 0x1f51b, 0x1f51c, 0x1f51a, 0x23f3, 0x231b, 0x23f0,
|
|
||||||
0x2648, 0x2649, 0x264a, 0x264b, 0x264c, 0x264d, 0x264e, 0x264f,
|
|
||||||
0x2650, 0x2651, 0x2652, 0x2653, 0x26ce, 0x1f531, 0x1f52f, 0x1f6bb,
|
|
||||||
0x1f6ae, 0x1f6af, 0x1f6b0, 0x1f6b1, 0x1f170, 0x1f171, 0x1f18e, 0x1f17e,
|
|
||||||
0x1f4ae, 0x1f4af, 0x1f520, 0x1f521, 0x1f522, 0x1f523, 0x1f524, 0x27bf,
|
|
||||||
0x1f4f6, 0x1f4f3, 0x1f4f4, 0x1f4f5, 0x1f6b9, 0x1f6ba, 0x1f6bc, 0x267f,
|
|
||||||
0x267b, 0x1f6ad, 0x1f6a9, 0x26a0, 0x1f201, 0x1f51e, 0x26d4, 0x1f192,
|
|
||||||
0x1f197, 0x1f195, 0x1f198, 0x1f199, 0x1f193, 0x1f196, 0x1f19a, 0x1f232,
|
|
||||||
0x1f233, 0x1f234, 0x1f235, 0x1f236, 0x1f237, 0x1f238, 0x1f239, 0x1f202,
|
|
||||||
0x1f23a, 0x1f250, 0x1f251, 0x3299, 0x00ae, 0x00a9, 0x2122, 0x1f21a,
|
|
||||||
0x1f22f, 0x3297, 0x2b55, 0x274c, 0x274e, 0x2139, 0x1f6ab, 0x2705,
|
|
||||||
0x2714, 0x1f517, 0x2734, 0x2733, 0x2795, 0x2796, 0x2716, 0x2797,
|
|
||||||
0x1f4a0, 0x1f4a1, 0x1f4a4, 0x1f4a2, 0x1f525, 0x1f4a5, 0x1f4a8, 0x1f4a6,
|
|
||||||
0x1f4ab, 0x1f55b, 0x1f567, 0x1f550, 0x1f55c, 0x1f551, 0x1f55d, 0x1f552,
|
|
||||||
0x1f55e, 0x1f553, 0x1f55f, 0x1f554, 0x1f560, 0x1f555, 0x1f561, 0x1f556,
|
|
||||||
0x1f562, 0x1f557, 0x1f563, 0x1f558, 0x1f564, 0x1f559, 0x1f565, 0x1f55a,
|
|
||||||
0x1f566, 0x2195, 0x2b06, 0x2197, 0x27a1, 0x2198, 0x2b07, 0x2199,
|
|
||||||
0x2b05, 0x2196, 0x2194, 0x2934, 0x2935, 0x23ea, 0x23eb, 0x23ec,
|
|
||||||
0x23e9, 0x25c0, 0x25b6, 0x1f53d, 0x1f53c, 0x2747, 0x2728, 0x1f534,
|
|
||||||
0x1f535, 0x26aa, 0x26ab, 0x1f533, 0x1f532, 0x2b50, 0x1f31f, 0x1f320,
|
|
||||||
0x25ab, 0x25aa, 0x25fd, 0x25fe, 0x25fb, 0x25fc, 0x2b1c, 0x2b1b,
|
|
||||||
0x1f538, 0x1f539, 0x1f536, 0x1f537, 0x1f53a, 0x1f53b, 0x1f51f, /*0x20e3,*/
|
|
||||||
0x2754, 0x2753, 0x2755, 0x2757, 0x203c, 0x2049, 0x3030, 0x27b0,
|
|
||||||
0x2660, 0x2665, 0x2663, 0x2666, 0x1f194, 0x1f511, 0x21a9, 0x1f191,
|
|
||||||
0x1f50d, 0x1f512, 0x1f513, 0x21aa, 0x1f510, 0x2611, 0x1f518, 0x1f50e,
|
|
||||||
0x1f516, 0x1f50f, 0x1f503, 0x1f500, 0x1f501, 0x1f502, 0x1f504, 0x1f4e7,
|
|
||||||
0x1f505, 0x1f506, 0x1f507, 0x1f508, 0x1f509, 0x1f50a
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final SparseArray<DrawInfo> offsets;
|
|
||||||
|
|
||||||
static {
|
|
||||||
offsets = new SparseArray<DrawInfo>();
|
|
||||||
for (int i = 0; i < PAGES.length; i++) {
|
|
||||||
for (int j = 0; j < PAGES[i].length; j++) {
|
|
||||||
offsets.put(PAGES[i][j], new DrawInfo(i, j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Bitmap[] bitmaps = new Bitmap[PAGES.length];
|
|
||||||
|
|
||||||
private static Emoji instance = null;
|
|
||||||
|
|
||||||
public synchronized static Emoji getInstance(Context context) {
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new Emoji(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("MalformedRegex")
|
|
||||||
// 0x20a0-0x32ff 0x1f00-0x1fff 0xfe4e5-0xfe4ee
|
|
||||||
// |==== misc ====||======== emoticons ========||========= flags ==========|
|
|
||||||
private static final Pattern EMOJI_RANGE = Pattern.compile("[\\u20a0-\\u32ff\\ud83c\\udc00-\\ud83d\\udeff\\udbb9\\udce5-\\udbb9\\udcee]");
|
|
||||||
|
|
||||||
public static final double EMOJI_HUGE = 1.00;
|
|
||||||
public static final double EMOJI_LARGE = 0.75;
|
|
||||||
public static final double EMOJI_SMALL = 0.50;
|
|
||||||
public static final int EMOJI_RAW_SIZE = 128;
|
|
||||||
public static final int EMOJI_PER_ROW = 16;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final int bigDrawSize;
|
|
||||||
|
|
||||||
private Emoji(Context context) {
|
|
||||||
this.context = context.getApplicationContext();
|
|
||||||
this.bigDrawSize = context.getResources().getDimensionPixelSize(R.dimen.emoji_drawer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadPage(final int page, final PageLoadedListener pageLoadListener) {
|
|
||||||
executor.submit(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
loadPage(page);
|
|
||||||
if (pageLoadListener != null) pageLoadListener.onPageLoaded();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w("Emoji", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadPage(int page) throws IOException {
|
|
||||||
if (page < 0 || page >= PAGES.length) {
|
|
||||||
throw new IndexOutOfBoundsException("can't load page that doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmaps[page] != null) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final String file = "emoji_" + page + "_wrapped.png";
|
|
||||||
final InputStream measureStream = context.getAssets().open(file);
|
|
||||||
final InputStream bitmapStream = context.getAssets().open(file);
|
|
||||||
|
|
||||||
bitmaps[page] = BitmapUtil.createScaledBitmap(measureStream, bitmapStream, (float) bigDrawSize / (float) EMOJI_RAW_SIZE);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w("Emoji", ioe);
|
|
||||||
throw ioe;
|
|
||||||
} catch (BitmapDecodingException bde) {
|
|
||||||
Log.w("Emoji", bde);
|
|
||||||
throw new AssertionError("emoji sprite asset is corrupted or android decoding is broken");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpannableString emojify(String text, PageLoadedListener pageLoadedListener) {
|
|
||||||
return emojify(new SpannableString(text), pageLoadedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpannableString emojify(SpannableString text, PageLoadedListener pageLoadedListener) {
|
|
||||||
return emojify(text, EMOJI_LARGE, pageLoadedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpannableString emojify(SpannableString text, double size, PageLoadedListener pageLoadedListener) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) return text;
|
|
||||||
|
|
||||||
Matcher matches = EMOJI_RANGE.matcher(text);
|
|
||||||
|
|
||||||
while (matches.find()) {
|
|
||||||
String resource = Integer.toHexString(matches.group().codePointAt(0));
|
|
||||||
|
|
||||||
Drawable drawable = getEmojiDrawable(resource, size, pageLoadedListener);
|
|
||||||
if (drawable != null) {
|
|
||||||
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
|
|
||||||
text.setSpan(imageSpan, matches.start(), matches.end(),
|
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pair<Integer, Drawable> getRecentlyUsed(int position, double size, PageLoadedListener pageLoadedListener) {
|
|
||||||
String[] recentlyUsed = EmojiLRU.getRecentlyUsed(context);
|
|
||||||
String code = recentlyUsed[recentlyUsed.length - 1 - position];
|
|
||||||
return new Pair<Integer, Drawable>(Integer.parseInt(code, 16), getEmojiDrawable(code, size, pageLoadedListener));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRecentlyUsed(String emojiCode) {
|
|
||||||
EmojiLRU.putRecentlyUsed(context, emojiCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRecentlyUsedAssetCount() {
|
|
||||||
return EmojiLRU.getRecentlyUsedCount(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Drawable getEmojiDrawable(String emojiCode, double size, PageLoadedListener pageLoadedListener) {
|
|
||||||
return getEmojiDrawable(offsets.get(Integer.parseInt(emojiCode, 16)), size, pageLoadedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Drawable getEmojiDrawable(DrawInfo drawInfo, double size, PageLoadedListener pageLoadedListener) {
|
|
||||||
if (drawInfo == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final Drawable drawable = new EmojiDrawable(drawInfo, bigDrawSize);
|
|
||||||
drawable.setBounds(0, 0, (int) ((double) bigDrawSize * size), (int) ((double) bigDrawSize * size));
|
|
||||||
if (bitmaps[drawInfo.page] == null) {
|
|
||||||
preloadPage(drawInfo.page, pageLoadedListener);
|
|
||||||
}
|
|
||||||
return drawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class EmojiLRU {
|
|
||||||
private static SharedPreferences prefs = null;
|
|
||||||
private static LinkedHashSet<String> recentlyUsed = null;
|
|
||||||
private static final String EMOJI_LRU_PREFERENCE = "pref_recent_emoji";
|
|
||||||
private static final int EMOJI_LRU_SIZE = 50;
|
|
||||||
|
|
||||||
private static void initializeCache(Context context) {
|
|
||||||
if (prefs == null) {
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
String serialized = prefs.getString(EMOJI_LRU_PREFERENCE, "[]");
|
|
||||||
|
|
||||||
try {
|
|
||||||
recentlyUsed = JsonUtils.getMapper().readValue(serialized, TypeFactory.defaultInstance()
|
|
||||||
.constructCollectionType(LinkedHashSet.class, String.class));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
recentlyUsed = new LinkedHashSet<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] getRecentlyUsed(Context context) {
|
|
||||||
if (recentlyUsed == null) initializeCache(context);
|
|
||||||
return recentlyUsed.toArray(new String[recentlyUsed.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getRecentlyUsedCount(Context context) {
|
|
||||||
if (recentlyUsed == null) initializeCache(context);
|
|
||||||
return recentlyUsed.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void putRecentlyUsed(Context context, String asset) {
|
|
||||||
if (recentlyUsed == null) initializeCache(context);
|
|
||||||
if (prefs == null) {
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
recentlyUsed.remove(asset);
|
|
||||||
recentlyUsed.add(asset);
|
|
||||||
|
|
||||||
if (recentlyUsed.size() > EMOJI_LRU_SIZE) {
|
|
||||||
Iterator<String> iterator = recentlyUsed.iterator();
|
|
||||||
iterator.next();
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
final LinkedHashSet<String> latestRecentlyUsed = new LinkedHashSet<String>(recentlyUsed);
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
try {
|
|
||||||
String serialized = JsonUtils.toJson(latestRecentlyUsed);
|
|
||||||
prefs.edit()
|
|
||||||
.putString(EMOJI_LRU_PREFERENCE, serialized)
|
|
||||||
.apply();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class EmojiDrawable extends Drawable {
|
|
||||||
private final int index;
|
|
||||||
private final int page;
|
|
||||||
private final int emojiSize;
|
|
||||||
private static final Paint paint;
|
|
||||||
private Bitmap bmp;
|
|
||||||
|
|
||||||
static {
|
|
||||||
paint = new Paint();
|
|
||||||
paint.setFilterBitmap(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EmojiDrawable(DrawInfo info, int emojiSize) {
|
|
||||||
this.index = info.index;
|
|
||||||
this.page = info.page;
|
|
||||||
this.emojiSize = emojiSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(Canvas canvas) {
|
|
||||||
if (bitmaps[page] == null) {
|
|
||||||
Log.w("Emoji", "bitmap for this page was null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (bmp == null) {
|
|
||||||
bmp = bitmaps[page];
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect b = copyBounds();
|
|
||||||
// int cX = b.centerX(), cY = b.centerY();
|
|
||||||
// b.left = cX - emojiSize / 2;
|
|
||||||
// b.right = cX + emojiSize / 2;
|
|
||||||
// b.top = cY - emojiSize / 2;
|
|
||||||
// b.bottom = cY + emojiSize / 2;
|
|
||||||
|
|
||||||
final int row = index / EMOJI_PER_ROW;
|
|
||||||
final int row_index = index % EMOJI_PER_ROW;
|
|
||||||
|
|
||||||
canvas.drawBitmap(bmp,
|
|
||||||
new Rect(row_index * emojiSize,
|
|
||||||
row * emojiSize,
|
|
||||||
(row_index + 1) * emojiSize,
|
|
||||||
(row + 1) * emojiSize),
|
|
||||||
b,
|
|
||||||
paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlpha(int alpha) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColorFilter(ColorFilter cf) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "EmojiDrawable{" +
|
|
||||||
"page=" + page +
|
|
||||||
", index=" + index +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface PageLoadedListener {
|
|
||||||
public void onPageLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class InvalidatingPageLoadedListener implements PageLoadedListener {
|
|
||||||
private final View view;
|
|
||||||
|
|
||||||
public InvalidatingPageLoadedListener(final View view) {
|
|
||||||
this.view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageLoaded() {
|
|
||||||
view.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
view.invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "InvalidatingPageLoadedListener{}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DrawInfo {
|
|
||||||
int page;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
public DrawInfo(final int page, final int index) {
|
|
||||||
this.page = page;
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "DrawInfo{" +
|
|
||||||
"page=" + page +
|
|
||||||
", index=" + index +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,6 +21,7 @@ import android.content.Context;
|
|||||||
import android.content.res.Resources.Theme;
|
import android.content.res.Resources.Theme;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.ArrayRes;
|
||||||
import android.support.annotation.AttrRes;
|
import android.support.annotation.AttrRes;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
@ -44,6 +45,16 @@ public class ResUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Drawable getDrawable(Context c, @AttrRes int attr) {
|
public static Drawable getDrawable(Context c, @AttrRes int attr) {
|
||||||
return c.getResources().getDrawable(getDrawableRes(c, attr));
|
return c.getResources().getDrawable(getDrawableRes(c, attr), c.getTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] getResourceIds(Context c, @ArrayRes int array) {
|
||||||
|
final TypedArray typedArray = c.getResources().obtainTypedArray(array);
|
||||||
|
final int[] resourceIds = new int[typedArray.length()];
|
||||||
|
for (int i = 0; i < typedArray.length(); i++) {
|
||||||
|
resourceIds[i] = typedArray.getResourceId(i, 0);
|
||||||
|
}
|
||||||
|
typedArray.recycle();
|
||||||
|
return resourceIds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user