Commit dca29c67 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Add skin tone selector to emoji keyboard

parent 8c73e88f
package chat.rocket.android.emoji
import android.annotation.SuppressLint
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
......@@ -9,7 +10,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
......@@ -24,8 +29,11 @@ class EmojiKeyboardPopup(
private lateinit var searchView: View
private lateinit var backspaceView: View
private lateinit var parentContainer: ViewGroup
private lateinit var changeColorView: View
private lateinit var adapter: EmojiPagerAdapter
var listener: EmojiKeyboardListener? = null
@SuppressLint("InflateParams")
override fun onCreateView(inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.emoji_keyboard, null)
parentContainer = view.findViewById(R.id.emoji_keyboard_container)
......@@ -33,6 +41,7 @@ class EmojiKeyboardPopup(
searchView = view.findViewById(R.id.emoji_search)
backspaceView = view.findViewById(R.id.emoji_backspace)
tabLayout = view.findViewById(R.id.tabs)
changeColorView = view.findViewById(R.id.color_change_view)
tabLayout.setupWithViewPager(viewPager)
return view
}
......@@ -44,11 +53,76 @@ class EmojiKeyboardPopup(
private fun setupBottomBar() {
searchView.setOnClickListener {
//TODO: search not yet implemented
}
backspaceView.setOnClickListener {
listener?.onNonEmojiKeyPressed(KeyEvent.KEYCODE_BACK)
}
changeColorView.setOnClickListener {
showSkinToneChooser()
}
}
private fun showSkinToneChooser() {
val view = LayoutInflater.from(context).inflate(R.layout.color_select_popup, null)
val dialog = AlertDialog.Builder(context)
.setView(view)
.setTitle("Default skin tone")
.setCancelable(true)
.create()
view.findViewById<ImageView>(R.id.default_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.Default)
}
view.findViewById<ImageView>(R.id.light_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.LightTone)
}
view.findViewById<ImageView>(R.id.medium_light_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.MediumLightTone)
}
view.findViewById<ImageView>(R.id.medium_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.MediumTone)
}
view.findViewById<ImageView>(R.id.medium_dark_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.MediumDarkTone)
}
view.findViewById<ImageView>(R.id.dark_image_view).setOnClickListener {
dialog.dismiss()
changeSkinTone(Fitzpatrick.DarkTone)
}
dialog.show()
}
private fun changeSkinTone(tone: Fitzpatrick) {
val drawable = ContextCompat.getDrawable(context, R.drawable.color_change_circle)!!
val wrappedDrawable = DrawableCompat.wrap(drawable)
DrawableCompat.setTint(wrappedDrawable, getFitzpatrickColor(tone))
(changeColorView as ImageView).setImageDrawable(wrappedDrawable)
adapter.setFitzpatrick(tone)
}
@ColorInt
private fun getFitzpatrickColor(tone: Fitzpatrick): Int {
return when(tone) {
Fitzpatrick.Default -> ContextCompat.getColor(context, R.color.tone_default)
Fitzpatrick.LightTone -> ContextCompat.getColor(context, R.color.tone_light)
Fitzpatrick.MediumLightTone -> ContextCompat.getColor(context, R.color.tone_medium_light)
Fitzpatrick.MediumTone -> ContextCompat.getColor(context, R.color.tone_medium)
Fitzpatrick.MediumDarkTone -> ContextCompat.getColor(context, R.color.tone_medium_dark)
Fitzpatrick.DarkTone -> ContextCompat.getColor(context, R.color.tone_dark)
}
}
private fun setupViewPager() {
......@@ -64,12 +138,14 @@ class EmojiKeyboardPopup(
}
}
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener {
adapter = EmojiPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
callback.onEmojiAdded(emoji)
}
})
viewPager.offscreenPageLimit = 0
viewPager.adapter = adapter
for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal)
......@@ -86,7 +162,8 @@ class EmojiKeyboardPopup(
}
class EmojiTextWatcher(private val editor: EditText) : TextWatcher {
@Volatile private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
@Volatile
private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
override fun afterTextChanged(s: Editable) {
val message = editor.editableText
......
......@@ -12,7 +12,10 @@ import androidx.viewpager.widget.PagerAdapter
import kotlinx.android.synthetic.main.emoji_category_layout.view.*
import java.util.*
internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) : PagerAdapter() {
internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) : PagerAdapter() {
private val adapters = hashMapOf<EmojiCategory, EmojiAdapter>()
private var fitzpatrick: Fitzpatrick = Fitzpatrick.Default
override fun isViewFromObject(view: View, obj: Any): Boolean {
return view == obj
......@@ -23,7 +26,7 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
.inflate(R.layout.emoji_category_layout, container, false)
with(view) {
val layoutManager = GridLayoutManager(context, 8)
val adapter = EmojiAdapter(layoutManager.spanCount, listener)
val adapter = EmojiAdapter(layoutManager.spanCount, listener = listener)
val category = EmojiCategory.values()[position]
val emojis = if (category != EmojiCategory.RECENTS) {
EmojiRepository.getEmojisByCategory(category)
......@@ -33,6 +36,8 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
val recentEmojiSize = EmojiRepository.getRecents().size
text_no_recent_emoji.isVisible = category == EmojiCategory.RECENTS && recentEmojiSize == 0
adapter.addEmojis(emojis)
adapter.setFitzpatrick(fitzpatrick)
adapters[category] = adapter
emoji_recycler_view.layoutManager = layoutManager
emoji_recycler_view.itemAnimator = DefaultItemAnimator()
emoji_recycler_view.adapter = adapter
......@@ -50,20 +55,46 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
override fun getPageTitle(position: Int) = EmojiCategory.values()[position].textIcon()
override fun getItemPosition(`object`: Any): Int {
return POSITION_NONE
}
fun setFitzpatrick(fitzpatrick: Fitzpatrick) {
this.fitzpatrick = fitzpatrick
for (entry in adapters.entries) {
if (entry.key != EmojiCategory.RECENTS) {
entry.value.setFitzpatrick(fitzpatrick)
}
}
}
class EmojiAdapter(
private val spanCount: Int,
private var fitzpatrick: Fitzpatrick = Fitzpatrick.Default,
private val listener: EmojiKeyboardListener
) : RecyclerView.Adapter<EmojiRowViewHolder>() {
private var emojis = Collections.emptyList<Emoji>()
init {
no++
}
fun addEmojis(emojis: List<Emoji>) {
this.emojis = emojis
notifyItemRangeInserted(0, emojis.size)
notifyDataSetChanged()
}
fun setFitzpatrick(fitzpatrick: Fitzpatrick) {
this.fitzpatrick = fitzpatrick
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: EmojiRowViewHolder, position: Int) {
holder.bind(emojis[position])
val emoji = emojis[position]
holder.bind(
emoji.siblings.find { it.fitzpatrick == fitzpatrick } ?: emoji
)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiRowViewHolder {
......@@ -72,6 +103,14 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
}
override fun getItemCount(): Int = emojis.size
override fun toString(): String {
return EmojiAdapter.no.toString()
}
companion object {
var no = 0
}
}
class EmojiRowViewHolder(
......
......@@ -3,8 +3,6 @@ package chat.rocket.android.emoji
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.viewpager.widget.ViewPager
import android.view.LayoutInflater
import android.view.Window
import android.view.WindowManager
......@@ -35,7 +33,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
}
private fun setupViewPager() {
pager_categories.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener {
pager_categories.adapter = EmojiPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
dismiss()
......
......@@ -68,7 +68,7 @@ object EmojiRepository {
}
private fun getFitzpatrick(type: String): Fitzpatrick {
return when(type) {
return when (type) {
Fitzpatrick.LightTone.type -> Fitzpatrick.LightTone
Fitzpatrick.MediumTone.type -> Fitzpatrick.MediumTone
Fitzpatrick.MediumLightTone.type -> Fitzpatrick.MediumLightTone
......
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/tone_default" />
<size
android:width="24dp"
android:height="24dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/light_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/medium_light_image_view"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/default_image_view"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/medium_light_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_medium_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/medium_image_view"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/light_image_view"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/medium_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/medium_dark_image_view"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/medium_light_image_view"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/medium_dark_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_medium_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/dark_image_view"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/medium_image_view"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/dark_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/medium_dark_image_view"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/default_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:tint="@color/tone_default"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/light_image_view"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/emoji_keyboard_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="wrap_content"
android:background="@color/colorWhite">
<View
......@@ -26,7 +26,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/emoji_actions_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
......@@ -36,29 +36,40 @@
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:id="@+id/emoji_search"
android:id="@+id/color_change_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/color_change_circle" />
<ImageView
android:id="@+id/emoji_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/whitesmoke"
android:clickable="true"
android:focusable="true"
android:padding="8dp"
android:src="@drawable/ic_search_gray_24px"
android:visibility="invisible" />
android:visibility="invisible"
app:layout_constraintEnd_toStartOf="@+id/emoji_backspace"
app:layout_constraintStart_toEndOf="@+id/color_change_view" />
<ImageView
android:id="@+id/emoji_backspace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@color/whitesmoke"
android:clickable="true"
android:focusable="true"
android:padding="8dp"
android:src="@drawable/ic_backspace_gray_24dp" />
</RelativeLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_backspace_gray_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -7,4 +7,12 @@
<color name="colorEmojiIcon">#FF767676</color>
<color name="colorDividerMessageComposer">#D8D8D8</color>
<!-- Skin tone colors -->
<color name="tone_default">#fdcb45</color>
<color name="tone_light">#fcd6b4</color>
<color name="tone_medium_light">#ecc0a1</color>
<color name="tone_medium">#ba8b6f</color>
<color name="tone_medium_dark">#926b4f</color>
<color name="tone_dark">#614833</color>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment