diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/adapters/EmojisAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/adapters/EmojisAdapter.kt index 48b7b167c..b27f20a14 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/adapters/EmojisAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/adapters/EmojisAdapter.kt @@ -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, val itemClick: (emoji: String) -> Unit) : RecyclerView.Adapter() { +class EmojisAdapter(val context: Context, private val items: List, val itemClick: (emoji: EmojiData) -> Unit) : + RecyclerView.Adapter() { 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 + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/EmojiHelper.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/EmojiHelper.kt index 89d622ad3..6e34c6558 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/EmojiHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/EmojiHelper.kt @@ -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? = null +private var cachedEmojiData: MutableList? = null val cachedVNTelexData: HashMap = HashMap() /** @@ -14,18 +15,21 @@ val cachedVNTelexData: HashMap = HashMap() * @param context The initiating view's context. * @param path The path to the asset file. */ -fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList { +fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList { if (cachedEmojiData != null) { return cachedEmojiData!! } - val emojis = mutableListOf() + val emojis = mutableListOf() var emojiEditorList: MutableList? = 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 } @@ -36,6 +40,7 @@ fun parseRawEmojiSpecsFile(context: Context, path: String): MutableList // Comment line } else if (line.startsWith("[")) { commitEmojiEditorList() + category = line.replace("[", "").replace("]", "") } else if (line.trim().isEmpty()) { // Empty line continue @@ -84,3 +89,22 @@ fun parseRawJsonSpecsFile(context: Context, path: String): HashMap +) { + 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 + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt index db67102a7..d164f899d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/views/MyKeyboardView.kt @@ -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.* @@ -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 @@ -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 @@ -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 @@ -1468,9 +1474,8 @@ 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() @@ -1478,7 +1483,7 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut } emojiPaletteBackspace.apply { - applyColorFilter(bottomTextColor) + applyColorFilter(textColor) setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { @@ -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 { @@ -1546,19 +1551,80 @@ class MyKeyboardView @JvmOverloads constructor(context: Context, attrs: Attribut } } - private fun setupEmojiAdapter(emojis: List) { + private fun setupEmojiAdapter(emojis: List) { + val categories = emojis.groupBy { it.category } + val allItems = mutableListOf() + categories.entries.forEach { (category, emojis) -> + allItems.add(EmojisAdapter.Item.Category(category)) + allItems.addAll(emojis.map(EmojisAdapter.Item::Emoji)) + } + val checkIds = mutableMapOf() + 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().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()?.forEach { + if (it.id == id) { + it.imageTintList = ColorStateList.valueOf(context.getProperPrimaryColor()) + } else { + it.imageTintList = ColorStateList.valueOf(mTextColor) + } + } + } + } } } } diff --git a/app/src/main/res/drawable/ic_emoji_category_activities.xml b/app/src/main/res/drawable/ic_emoji_category_activities.xml new file mode 100644 index 000000000..490be0742 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_activities.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_animals.xml b/app/src/main/res/drawable/ic_emoji_category_animals.xml new file mode 100644 index 000000000..1b92b4d62 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_animals.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_flags.xml b/app/src/main/res/drawable/ic_emoji_category_flags.xml new file mode 100644 index 000000000..138d59f98 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_flags.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_food.xml b/app/src/main/res/drawable/ic_emoji_category_food.xml new file mode 100644 index 000000000..5d34b3d70 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_food.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_objects.xml b/app/src/main/res/drawable/ic_emoji_category_objects.xml new file mode 100644 index 000000000..e583c549e --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_objects.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_people.xml b/app/src/main/res/drawable/ic_emoji_category_people.xml new file mode 100644 index 000000000..b48d19e46 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_people.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_smileys.xml b/app/src/main/res/drawable/ic_emoji_category_smileys.xml new file mode 100644 index 000000000..338e483c4 --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_smileys.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_symbols.xml b/app/src/main/res/drawable/ic_emoji_category_symbols.xml new file mode 100644 index 000000000..59c06cdee --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_symbols.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_emoji_category_travel.xml b/app/src/main/res/drawable/ic_emoji_category_travel.xml new file mode 100644 index 000000000..e1aa314cc --- /dev/null +++ b/app/src/main/res/drawable/ic_emoji_category_travel.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/emoji_list.xml b/app/src/main/res/layout/emoji_list.xml new file mode 100644 index 000000000..5baf16714 --- /dev/null +++ b/app/src/main/res/layout/emoji_list.xml @@ -0,0 +1,8 @@ + diff --git a/app/src/main/res/layout/item_emoji_category.xml b/app/src/main/res/layout/item_emoji_category.xml new file mode 100644 index 000000000..0942705af --- /dev/null +++ b/app/src/main/res/layout/item_emoji_category.xml @@ -0,0 +1,11 @@ + + diff --git a/app/src/main/res/layout/keyboard_view_keyboard.xml b/app/src/main/res/layout/keyboard_view_keyboard.xml index 83e2f8d83..5883afcd5 100644 --- a/app/src/main/res/layout/keyboard_view_keyboard.xml +++ b/app/src/main/res/layout/keyboard_view_keyboard.xml @@ -202,6 +202,18 @@ android:text="@string/abc" android:textStyle="bold" /> + + -10dp 28dp 46dp + 24dp 4dp 42dp 200dp @@ -17,4 +18,5 @@ 26sp 16sp 28sp + 14sp