Skip to content

Commit

Permalink
Merge pull request #232 from esensar/feature/229-emoji-categories
Browse files Browse the repository at this point in the history
Add emoji categories to emoji list
  • Loading branch information
tibbi authored Aug 30, 2023
2 parents 6fd4eb5 + 8137b71 commit 25918d4
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,72 @@ import android.view.View
import android.view.ViewGroup
import androidx.emoji2.text.EmojiCompat
import androidx.recyclerview.widget.RecyclerView
import com.simplemobiletools.commons.databinding.DividerBinding
import com.simplemobiletools.commons.extensions.beInvisible
import com.simplemobiletools.keyboard.databinding.ItemEmojiBinding
import com.simplemobiletools.keyboard.helpers.EmojiData

class EmojisAdapter(val context: Context, var items: List<String>, val itemClick: (emoji: String) -> Unit) : RecyclerView.Adapter<EmojisAdapter.ViewHolder>() {
class EmojisAdapter(val context: Context, private val items: List<Item>, val itemClick: (emoji: EmojiData) -> Unit) :
RecyclerView.Adapter<EmojisAdapter.ViewHolder>() {
private val layoutInflater = LayoutInflater.from(context)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojisAdapter.ViewHolder {
val view = ItemEmojiBinding.inflate(layoutInflater, parent, false).root
return ViewHolder(view)
return when (viewType) {
ITEM_TYPE_EMOJI -> {
val view = ItemEmojiBinding.inflate(layoutInflater, parent, false).root
ViewHolder(view)
}

else -> {
val view = DividerBinding.inflate(layoutInflater, parent, false).root.apply { beInvisible() }
ViewHolder(view)
}
}
}

override fun onBindViewHolder(holder: EmojisAdapter.ViewHolder, position: Int) {
val item = items[position]
holder.bindView(item) { itemView ->
setupEmoji(itemView, item)
if (item is Item.Emoji) {
holder.bindView(item) { itemView ->
setupEmoji(itemView, item)
}
}
}

override fun getItemViewType(position: Int): Int {
return if (items[position] is Item.Emoji) {
ITEM_TYPE_EMOJI
} else {
ITEM_TYPE_CATEGORY
}
}

override fun getItemCount() = items.size

private fun setupEmoji(view: View, emoji: String) {
val processed = EmojiCompat.get().process(emoji)
private fun setupEmoji(view: View, emoji: Item.Emoji) {
val processed = EmojiCompat.get().process(emoji.value.emoji)
ItemEmojiBinding.bind(view).emojiValue.text = processed
}

inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindView(emoji: String, callback: (itemView: View) -> Unit): View {
fun bindView(emoji: Item.Emoji, callback: (itemView: View) -> Unit): View {
return itemView.apply {
callback(this)

setOnClickListener {
itemClick.invoke(emoji)
itemClick.invoke(emoji.value)
}
}
}
}

sealed interface Item {
data class Emoji(val value: EmojiData) : Item
data class Category(val value: String) : Item
}

companion object {
private const val ITEM_TYPE_EMOJI = 0
private const val ITEM_TYPE_CATEGORY = 1
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.simplemobiletools.keyboard.helpers

import android.content.Context
import com.simplemobiletools.keyboard.R
import org.json.JSONObject
import java.io.InputStream

private var cachedEmojiData: MutableList<String>? = null
private var cachedEmojiData: MutableList<EmojiData>? = null
val cachedVNTelexData: HashMap<String, String> = HashMap()

/**
Expand All @@ -14,18 +15,21 @@ val cachedVNTelexData: HashMap<String, String> = HashMap()
* @param context The initiating view's context.
* @param path The path to the asset file.
*/
fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList<String> {
fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList<EmojiData> {
if (cachedEmojiData != null) {
return cachedEmojiData!!
}

val emojis = mutableListOf<String>()
val emojis = mutableListOf<EmojiData>()
var emojiEditorList: MutableList<String>? = null
var category: String? = null

fun commitEmojiEditorList() {
emojiEditorList?.let {
// add only the base emoji for now, ignore the variations
emojis.add(it.first())
val base = it.first()
val variants = it.drop(1)
emojis.add(EmojiData(category ?: "none", base, variants))
}
emojiEditorList = null
}
Expand All @@ -36,6 +40,7 @@ fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList<String>
// Comment line
} else if (line.startsWith("[")) {
commitEmojiEditorList()
category = line.replace("[", "").replace("]", "")
} else if (line.trim().isEmpty()) {
// Empty line
continue
Expand Down Expand Up @@ -84,3 +89,22 @@ fun parseRawJsonSpecsFile(context: Context, path: String): HashMap<String, Strin
}
return cachedVNTelexData
}

data class EmojiData(
val category: String,
val emoji: String,
val variants: List<String>
) {
fun getCategoryIcon(): Int =
when (category) {
"people_body" -> R.drawable.ic_emoji_category_people
"animals_nature" -> R.drawable.ic_emoji_category_animals
"food_drink" -> R.drawable.ic_emoji_category_food
"travel_places" -> R.drawable.ic_emoji_category_travel
"activities" -> R.drawable.ic_emoji_category_activities
"objects" -> R.drawable.ic_emoji_category_objects
"symbols" -> R.drawable.ic_emoji_category_symbols
"flags" -> R.drawable.ic_emoji_category_flags
else -> R.drawable.ic_emoji_category_smileys
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.*
import android.graphics.Paint.Align
import android.graphics.drawable.*
Expand All @@ -19,6 +20,7 @@ import android.util.TypedValue
import android.view.*
import android.view.animation.AccelerateInterpolator
import android.view.inputmethod.EditorInfo
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
Expand All @@ -29,6 +31,9 @@ import androidx.core.animation.doOnStart
import androidx.core.view.*
import androidx.emoji2.text.EmojiCompat
import androidx.emoji2.text.EmojiCompat.EMOJI_SUPPORTED
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import androidx.recyclerview.widget.LinearLayoutManager
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.isPiePlus
Expand All @@ -37,6 +42,7 @@ import com.simplemobiletools.keyboard.activities.ManageClipboardItemsActivity
import com.simplemobiletools.keyboard.activities.SettingsActivity
import com.simplemobiletools.keyboard.adapters.ClipsKeyboardAdapter
import com.simplemobiletools.keyboard.adapters.EmojisAdapter
import com.simplemobiletools.keyboard.databinding.ItemEmojiCategoryBinding
import com.simplemobiletools.keyboard.databinding.KeyboardKeyPreviewBinding
import com.simplemobiletools.keyboard.databinding.KeyboardPopupKeyboardBinding
import com.simplemobiletools.keyboard.databinding.KeyboardViewKeyboardBinding
Expand Down Expand Up @@ -1468,17 +1474,16 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
emojiPaletteLabel.setTextColor(textColor)

emojiPaletteBottomBar.background = ColorDrawable(backgroundColor)
val bottomTextColor = textColor.darkenColor()
emojiPaletteModeChange.apply {
setTextColor(bottomTextColor)
setTextColor(textColor)
setOnClickListener {
vibrateIfNeeded()
closeEmojiPalette()
}
}

emojiPaletteBackspace.apply {
applyColorFilter(bottomTextColor)
applyColorFilter(textColor)
setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Expand Down Expand Up @@ -1529,8 +1534,8 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
}

val emojis = fullEmojiList.filter { emoji ->
systemFontPaint.hasGlyph(emoji) || (EmojiCompat.get().loadState == EmojiCompat.LOAD_STATE_SUCCEEDED && EmojiCompat.get()
.getEmojiMatch(emoji, emojiCompatMetadataVersion) == EMOJI_SUPPORTED)
systemFontPaint.hasGlyph(emoji.emoji) || (EmojiCompat.get().loadState == EmojiCompat.LOAD_STATE_SUCCEEDED && EmojiCompat.get()
.getEmojiMatch(emoji.emoji, emojiCompatMetadataVersion) == EMOJI_SUPPORTED)
}

Handler(Looper.getMainLooper()).post {
Expand All @@ -1546,19 +1551,80 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut
}
}

private fun setupEmojiAdapter(emojis: List<String>) {
private fun setupEmojiAdapter(emojis: List<EmojiData>) {
val categories = emojis.groupBy { it.category }
val allItems = mutableListOf<EmojisAdapter.Item>()
categories.entries.forEach { (category, emojis) ->
allItems.add(EmojisAdapter.Item.Category(category))
allItems.addAll(emojis.map(EmojisAdapter.Item::Emoji))
}
val checkIds = mutableMapOf<Int, String>()
keyboardViewBinding?.emojiCategoriesStrip?.apply {
weightSum = categories.count().toFloat()
val strip = this
removeAllViews()
categories.entries.forEach { (category, emojis) ->
ItemEmojiCategoryBinding.inflate(LayoutInflater.from(context), this, true).apply {
root.id = generateViewId()
checkIds[root.id] = category
root.setImageResource(emojis.first().getCategoryIcon())
root.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
1f
)
root.setOnClickListener {
strip.children.filterIsInstance<ImageButton>().forEach {
it.imageTintList = ColorStateList.valueOf(mTextColor)
}
root.imageTintList = ColorStateList.valueOf(context.getProperPrimaryColor())
keyboardViewBinding?.emojisList?.stopScroll()
(keyboardViewBinding?.emojisList?.layoutManager as? GridLayoutManager)?.scrollToPositionWithOffset(
allItems.indexOfFirst { it is EmojisAdapter.Item.Category && it.value == category },
0
)
}
root.imageTintList = ColorStateList.valueOf(mTextColor)
}
}
}
keyboardViewBinding?.emojisList?.apply {
val emojiItemWidth = context.resources.getDimensionPixelSize(R.dimen.emoji_item_size)
val emojiTopBarElevation = context.resources.getDimensionPixelSize(R.dimen.emoji_top_bar_elevation).toFloat()

layoutManager = AutoGridLayoutManager(context, emojiItemWidth)
adapter = EmojisAdapter(context = context, items = emojis) { emoji ->
mOnKeyboardActionListener!!.onText(emoji)
layoutManager = AutoGridLayoutManager(context, emojiItemWidth).apply {
spanSizeLookup = object : SpanSizeLookup() {
override fun getSpanSize(position: Int): Int =
if (allItems[position] is EmojisAdapter.Item.Category) {
spanCount
} else {
1
}
}
}
adapter = EmojisAdapter(context = context, items = allItems) { emoji ->
mOnKeyboardActionListener!!.onText(emoji.emoji)
vibrateIfNeeded()
}

clearOnScrollListeners()
onScroll {
keyboardViewBinding!!.emojiPaletteTopBar.elevation = if (it > 4) emojiTopBarElevation else 0f
(keyboardViewBinding?.emojisList?.layoutManager as? LinearLayoutManager)?.findFirstCompletelyVisibleItemPosition()?.also { firstVisibleIndex ->
allItems
.withIndex()
.lastOrNull { it.value is EmojisAdapter.Item.Category && it.index <= firstVisibleIndex }
?.also { activeCategory ->
val id = checkIds.entries.first { it.value == (activeCategory.value as EmojisAdapter.Item.Category).value }.key
keyboardViewBinding?.emojiCategoriesStrip?.children?.filterIsInstance<ImageButton>()?.forEach {
if (it.id == id) {
it.imageTintList = ColorStateList.valueOf(context.getProperPrimaryColor())
} else {
it.imageTintList = ColorStateList.valueOf(mTextColor)
}
}
}
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_activities.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,5h-2V3H7v2H5C3.9,5 3,5.9 3,7v1c0,2.55 1.92,4.63 4.39,4.94c0.63,1.5 1.98,2.63 3.61,2.96V19H7v2h10v-2h-4v-3.1c1.63,-0.33 2.98,-1.46 3.61,-2.96C19.08,12.63 21,10.55 21,8V7C21,5.9 20.1,5 19,5zM5,8V7h2v3.82C5.84,10.4 5,9.3 5,8zM19,8c0,1.3 -0.84,2.4 -2,2.82V7h2V8z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_animals.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21.94,4.88C21.76,4.35 21.25,4 20.68,4c-0.03,0 -0.06,0 -0.09,0H19.6l-0.31,-0.97C19.15,2.43 18.61,2 18,2h0c-0.61,0 -1.15,0.43 -1.29,1.04L16.4,4h-0.98c-0.03,0 -0.06,0 -0.09,0c-0.57,0 -1.08,0.35 -1.26,0.88c-0.19,0.56 0.04,1.17 0.56,1.48l0.87,0.52L15.1,8.12c-0.23,0.58 -0.04,1.25 0.45,1.62C15.78,9.91 16.06,10 16.33,10c0.31,0 0.61,-0.11 0.86,-0.32L18,8.98l0.81,0.7C19.06,9.89 19.36,10 19.67,10c0.27,0 0.55,-0.09 0.78,-0.26c0.5,-0.37 0.68,-1.04 0.45,-1.62l-0.39,-1.24l0.87,-0.52C21.89,6.05 22.12,5.44 21.94,4.88zM18,7c-0.55,0 -1,-0.45 -1,-1c0,-0.55 0.45,-1 1,-1s1,0.45 1,1C19,6.55 18.55,7 18,7z"/>
<path android:fillColor="@android:color/white" android:pathData="M13.49,10.51c-0.43,-0.43 -0.94,-0.73 -1.49,-0.93V8h-1v1.38c-0.11,-0.01 -0.23,-0.03 -0.34,-0.03c-1.02,0 -2.05,0.39 -2.83,1.17c-0.16,0.16 -0.3,0.34 -0.43,0.53L6,10.52c-1.56,-0.55 -3.28,0.27 -3.83,1.82c0,0 0,0 0,0c-0.27,0.75 -0.23,1.57 0.12,2.29c0.23,0.48 0.58,0.87 1,1.16c-0.38,1.35 -0.06,2.85 1,3.91c1.06,1.06 2.57,1.38 3.91,1c0.29,0.42 0.68,0.77 1.16,1C9.78,21.9 10.21,22 10.65,22c0.34,0 0.68,-0.06 1.01,-0.17c0,0 0,0 0,0c1.56,-0.55 2.38,-2.27 1.82,-3.85l-0.52,-1.37c0.18,-0.13 0.36,-0.27 0.53,-0.43c0.87,-0.87 1.24,-2.04 1.14,-3.17H16v-1h-1.59C14.22,11.46 13.92,10.95 13.49,10.51zM4.67,14.29c-0.25,-0.09 -0.45,-0.27 -0.57,-0.51s-0.13,-0.51 -0.04,-0.76c0.19,-0.52 0.76,-0.79 1.26,-0.61l3.16,1.19C7.33,14.2 5.85,14.71 4.67,14.29zM10.99,19.94c-0.25,0.09 -0.52,0.08 -0.76,-0.04c-0.24,-0.11 -0.42,-0.32 -0.51,-0.57c-0.42,-1.18 0.09,-2.65 0.7,-3.8l1.18,3.13C11.78,19.18 11.51,19.76 10.99,19.94zM12.2,14.6l-0.61,-1.61c0,-0.01 -0.01,-0.02 -0.02,-0.03c-0.02,-0.04 -0.04,-0.08 -0.06,-0.12c-0.02,-0.04 -0.04,-0.07 -0.07,-0.11c-0.03,-0.03 -0.06,-0.06 -0.09,-0.09c-0.03,-0.03 -0.06,-0.06 -0.09,-0.09c-0.03,-0.03 -0.07,-0.05 -0.11,-0.07c-0.04,-0.02 -0.07,-0.05 -0.12,-0.06c-0.01,0 -0.02,-0.01 -0.03,-0.02L9.4,11.8c0.36,-0.29 0.79,-0.46 1.26,-0.46c0.53,0 1.04,0.21 1.41,0.59C12.8,12.66 12.84,13.81 12.2,14.6z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_flags.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_food.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,3H9v2.4l1.81,1.45C10.93,6.94 11,7.09 11,7.24v4.26c0,0.28 -0.22,0.5 -0.5,0.5h-4C6.22,12 6,11.78 6,11.5V7.24c0,-0.15 0.07,-0.3 0.19,-0.39L8,5.4V3H4v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.9 2,-2V5C22,3.89 21.11,3 20,3zM20,8h-2V5h2V8z"/>
<path android:fillColor="@android:color/white" android:pathData="M4,19h16v2h-16z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_objects.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,3c-0.46,0 -0.93,0.04 -1.4,0.14C7.84,3.67 5.64,5.9 5.12,8.66c-0.48,2.61 0.48,5.01 2.22,6.56C7.77,15.6 8,16.13 8,16.69V19c0,1.1 0.9,2 2,2h0.28c0.35,0.6 0.98,1 1.72,1s1.38,-0.4 1.72,-1H14c1.1,0 2,-0.9 2,-2v-2.31c0,-0.55 0.22,-1.09 0.64,-1.46C18.09,13.95 19,12.08 19,10C19,6.13 15.87,3 12,3zM14,19h-4v-1h4V19zM14,17h-4v-1h4V17zM12.5,11.41V14h-1v-2.59L9.67,9.59l0.71,-0.71L12,10.5l1.62,-1.62l0.71,0.71L12.5,11.41z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_people.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,4m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
<path android:fillColor="@android:color/white" android:pathData="M15.89,8.11C15.5,7.72 14.83,7 13.53,7c-0.21,0 -1.42,0 -2.54,0C8.24,6.99 6,4.75 6,2H4c0,3.16 2.11,5.84 5,6.71V22h2v-6h2v6h2V10.05L18.95,14l1.41,-1.41L15.89,8.11z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_emoji_category_smileys.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12c0,5.52 4.47,10 9.99,10C17.52,22 22,17.52 22,12C22,6.48 17.52,2 11.99,2zM8.5,8C9.33,8 10,8.67 10,9.5S9.33,11 8.5,11S7,10.33 7,9.5S7.67,8 8.5,8zM12,18c-2.28,0 -4.22,-1.66 -5,-4h10C16.22,16.34 14.28,18 12,18zM15.5,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S14.67,8 15.5,8S17,8.67 17,9.5S16.33,11 15.5,11z"/>
</vector>
Loading

0 comments on commit 25918d4

Please sign in to comment.