Commit dca29c67 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Add skin tone selector to emoji keyboard

parent 8c73e88f
package chat.rocket.android.emoji package chat.rocket.android.emoji
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
...@@ -9,7 +10,11 @@ import android.view.View ...@@ -9,7 +10,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
...@@ -24,8 +29,11 @@ class EmojiKeyboardPopup( ...@@ -24,8 +29,11 @@ class EmojiKeyboardPopup(
private lateinit var searchView: View private lateinit var searchView: View
private lateinit var backspaceView: View private lateinit var backspaceView: View
private lateinit var parentContainer: ViewGroup private lateinit var parentContainer: ViewGroup
private lateinit var changeColorView: View
private lateinit var adapter: EmojiPagerAdapter
var listener: EmojiKeyboardListener? = null var listener: EmojiKeyboardListener? = null
@SuppressLint("InflateParams")
override fun onCreateView(inflater: LayoutInflater): View { override fun onCreateView(inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.emoji_keyboard, null) val view = inflater.inflate(R.layout.emoji_keyboard, null)
parentContainer = view.findViewById(R.id.emoji_keyboard_container) parentContainer = view.findViewById(R.id.emoji_keyboard_container)
...@@ -33,6 +41,7 @@ class EmojiKeyboardPopup( ...@@ -33,6 +41,7 @@ class EmojiKeyboardPopup(
searchView = view.findViewById(R.id.emoji_search) searchView = view.findViewById(R.id.emoji_search)
backspaceView = view.findViewById(R.id.emoji_backspace) backspaceView = view.findViewById(R.id.emoji_backspace)
tabLayout = view.findViewById(R.id.tabs) tabLayout = view.findViewById(R.id.tabs)
changeColorView = view.findViewById(R.id.color_change_view)
tabLayout.setupWithViewPager(viewPager) tabLayout.setupWithViewPager(viewPager)
return view return view
} }
...@@ -44,11 +53,76 @@ class EmojiKeyboardPopup( ...@@ -44,11 +53,76 @@ class EmojiKeyboardPopup(
private fun setupBottomBar() { private fun setupBottomBar() {
searchView.setOnClickListener { searchView.setOnClickListener {
//TODO: search not yet implemented
} }
backspaceView.setOnClickListener { backspaceView.setOnClickListener {
listener?.onNonEmojiKeyPressed(KeyEvent.KEYCODE_BACK) 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() { private fun setupViewPager() {
...@@ -64,12 +138,14 @@ class EmojiKeyboardPopup( ...@@ -64,12 +138,14 @@ class EmojiKeyboardPopup(
} }
} }
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener { adapter = EmojiPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) { override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji) EmojiRepository.addToRecents(emoji)
callback.onEmojiAdded(emoji) callback.onEmojiAdded(emoji)
} }
}) })
viewPager.offscreenPageLimit = 0
viewPager.adapter = adapter
for (category in EmojiCategory.values()) { for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal) val tab = tabLayout.getTabAt(category.ordinal)
...@@ -86,7 +162,8 @@ class EmojiKeyboardPopup( ...@@ -86,7 +162,8 @@ class EmojiKeyboardPopup(
} }
class EmojiTextWatcher(private val editor: EditText) : TextWatcher { class EmojiTextWatcher(private val editor: EditText) : TextWatcher {
@Volatile private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>() @Volatile
private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
val message = editor.editableText val message = editor.editableText
......
...@@ -12,7 +12,10 @@ import androidx.viewpager.widget.PagerAdapter ...@@ -12,7 +12,10 @@ import androidx.viewpager.widget.PagerAdapter
import kotlinx.android.synthetic.main.emoji_category_layout.view.* import kotlinx.android.synthetic.main.emoji_category_layout.view.*
import java.util.* 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 { override fun isViewFromObject(view: View, obj: Any): Boolean {
return view == obj return view == obj
...@@ -23,7 +26,7 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) ...@@ -23,7 +26,7 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
.inflate(R.layout.emoji_category_layout, container, false) .inflate(R.layout.emoji_category_layout, container, false)
with(view) { with(view) {
val layoutManager = GridLayoutManager(context, 8) val layoutManager = GridLayoutManager(context, 8)
val adapter = EmojiAdapter(layoutManager.spanCount, listener) val adapter = EmojiAdapter(layoutManager.spanCount, listener = listener)
val category = EmojiCategory.values()[position] val category = EmojiCategory.values()[position]
val emojis = if (category != EmojiCategory.RECENTS) { val emojis = if (category != EmojiCategory.RECENTS) {
EmojiRepository.getEmojisByCategory(category) EmojiRepository.getEmojisByCategory(category)
...@@ -33,6 +36,8 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) ...@@ -33,6 +36,8 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
val recentEmojiSize = EmojiRepository.getRecents().size val recentEmojiSize = EmojiRepository.getRecents().size
text_no_recent_emoji.isVisible = category == EmojiCategory.RECENTS && recentEmojiSize == 0 text_no_recent_emoji.isVisible = category == EmojiCategory.RECENTS && recentEmojiSize == 0
adapter.addEmojis(emojis) adapter.addEmojis(emojis)
adapter.setFitzpatrick(fitzpatrick)
adapters[category] = adapter
emoji_recycler_view.layoutManager = layoutManager emoji_recycler_view.layoutManager = layoutManager
emoji_recycler_view.itemAnimator = DefaultItemAnimator() emoji_recycler_view.itemAnimator = DefaultItemAnimator()
emoji_recycler_view.adapter = adapter emoji_recycler_view.adapter = adapter
...@@ -50,20 +55,46 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) ...@@ -50,20 +55,46 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
override fun getPageTitle(position: Int) = EmojiCategory.values()[position].textIcon() 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( class EmojiAdapter(
private val spanCount: Int, private val spanCount: Int,
private var fitzpatrick: Fitzpatrick = Fitzpatrick.Default,
private val listener: EmojiKeyboardListener private val listener: EmojiKeyboardListener
) : RecyclerView.Adapter<EmojiRowViewHolder>() { ) : RecyclerView.Adapter<EmojiRowViewHolder>() {
private var emojis = Collections.emptyList<Emoji>() private var emojis = Collections.emptyList<Emoji>()
init {
no++
}
fun addEmojis(emojis: List<Emoji>) { fun addEmojis(emojis: List<Emoji>) {
this.emojis = emojis this.emojis = emojis
notifyItemRangeInserted(0, emojis.size) notifyDataSetChanged()
}
fun setFitzpatrick(fitzpatrick: Fitzpatrick) {
this.fitzpatrick = fitzpatrick
notifyDataSetChanged()
} }
override fun onBindViewHolder(holder: EmojiRowViewHolder, position: Int) { 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 { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiRowViewHolder {
...@@ -72,6 +103,14 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) ...@@ -72,6 +103,14 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
} }
override fun getItemCount(): Int = emojis.size override fun getItemCount(): Int = emojis.size
override fun toString(): String {
return EmojiAdapter.no.toString()
}
companion object {
var no = 0
}
} }
class EmojiRowViewHolder( class EmojiRowViewHolder(
......
...@@ -3,8 +3,6 @@ package chat.rocket.android.emoji ...@@ -3,8 +3,6 @@ package chat.rocket.android.emoji
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.viewpager.widget.ViewPager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Window import android.view.Window
import android.view.WindowManager import android.view.WindowManager
...@@ -35,7 +33,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) { ...@@ -35,7 +33,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
} }
private fun setupViewPager() { private fun setupViewPager() {
pager_categories.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener { pager_categories.adapter = EmojiPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) { override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji) EmojiRepository.addToRecents(emoji)
dismiss() dismiss()
......
...@@ -68,7 +68,7 @@ object EmojiRepository { ...@@ -68,7 +68,7 @@ object EmojiRepository {
} }
private fun getFitzpatrick(type: String): Fitzpatrick { private fun getFitzpatrick(type: String): Fitzpatrick {
return when(type) { return when (type) {
Fitzpatrick.LightTone.type -> Fitzpatrick.LightTone Fitzpatrick.LightTone.type -> Fitzpatrick.LightTone
Fitzpatrick.MediumTone.type -> Fitzpatrick.MediumTone Fitzpatrick.MediumTone.type -> Fitzpatrick.MediumTone
Fitzpatrick.MediumLightTone.type -> Fitzpatrick.MediumLightTone 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 @@ ...@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/emoji_keyboard_container" android:id="@+id/emoji_keyboard_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:background="@color/colorWhite"> android:background="@color/colorWhite">
<View <View
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/emoji_actions_container" android:id="@+id/emoji_actions_container"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
...@@ -36,29 +36,40 @@ ...@@ -36,29 +36,40 @@
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintStart_toStartOf="parent">
<ImageView <ImageView
android:id="@+id/emoji_search" android:id="@+id/color_change_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:padding="8dp"
android:layout_centerVertical="true" 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:background="@color/whitesmoke"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:padding="8dp" android:padding="8dp"
android:src="@drawable/ic_search_gray_24px" 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 <ImageView
android:id="@+id/emoji_backspace" android:id="@+id/emoji_backspace"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@color/whitesmoke" android:background="@color/whitesmoke"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:padding="8dp" android:padding="8dp"
android:src="@drawable/ic_backspace_gray_24dp" /> app:layout_constraintBottom_toBottomOf="parent"
</RelativeLayout> 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> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -7,4 +7,12 @@ ...@@ -7,4 +7,12 @@
<color name="colorEmojiIcon">#FF767676</color> <color name="colorEmojiIcon">#FF767676</color>
<color name="colorDividerMessageComposer">#D8D8D8</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> </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