Commit 2b3858b1 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Move emoji keyboard from fragment approach to popup approach.

parent bc77daa5
...@@ -37,19 +37,22 @@ ...@@ -37,19 +37,22 @@
<activity <activity
android:name=".main.ui.MainActivity" android:name=".main.ui.MainActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity <activity
android:name=".webview.WebViewActivity" android:name=".webview.WebViewActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity <activity
android:name=".chatroom.ui.ChatRoomActivity" android:name=".chatroom.ui.ChatRoomActivity"
android:windowSoftInputMode="adjustPan" android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity <activity
android:name=".chatroom.ui.PinnedMessagesActivity" android:name=".chatroom.ui.PinnedMessagesActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<receiver <receiver
......
...@@ -302,10 +302,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -302,10 +302,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} }
} }
fun dispatchRestoreUIState() {
view.restoreUIState()
}
private suspend fun listenMessages(roomId: String) { private suspend fun listenMessages(roomId: String) {
launch(CommonPool + strategy.jobs) { launch(CommonPool + strategy.jobs) {
for (message in client.messagesChannel) { for (message in client.messagesChannel) {
......
...@@ -97,9 +97,4 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -97,9 +97,4 @@ interface ChatRoomView : LoadingView, MessageView {
fun clearMessageComposition() fun clearMessageComposition()
fun showInvalidFileSize(fileSize: Int, maxFileSize: Int) fun showInvalidFileSize(fileSize: Int, maxFileSize: Int)
/**
* Restore UI state.
*/
fun restoreUIState()
} }
\ No newline at end of file
...@@ -5,10 +5,8 @@ import android.content.Intent ...@@ -5,10 +5,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.widget.emoji.EmojiFragment
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
...@@ -67,14 +65,7 @@ class ChatRoomActivity : SwipeBackActivity(), HasSupportFragmentInjector { ...@@ -67,14 +65,7 @@ class ChatRoomActivity : SwipeBackActivity(), HasSupportFragmentInjector {
} }
override fun onBackPressed() { override fun onBackPressed() {
val frag = supportFragmentManager.findFragmentByTag(EmojiFragment.TAG) as EmojiFragment?
if (frag != null && frag.isExpanded()) {
frag.collapse()
} else {
KeyboardHelper.hideSoftKeyboard(this)
finishActivity() finishActivity()
super.onBackPressed()
}
} }
override fun supportFragmentInjector(): AndroidInjector<Fragment> { override fun supportFragmentInjector(): AndroidInjector<Fragment> {
......
...@@ -96,7 +96,6 @@ class ChatRoomAdapter(private val roomType: String, ...@@ -96,7 +96,6 @@ class ChatRoomAdapter(private val roomType: String,
setChecked(isPinned) setChecked(isPinned)
} }
val adapter = ActionListAdapter(menuItems, this@ViewHolder) val adapter = ActionListAdapter(menuItems, this@ViewHolder)
presenter.dispatchRestoreUIState()
BottomSheetMenu(adapter).show(it.context) BottomSheetMenu(adapter).show(it.context)
} }
} }
......
...@@ -24,7 +24,7 @@ import chat.rocket.android.helper.MessageParser ...@@ -24,7 +24,7 @@ import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.android.widget.emoji.ComposerEditText import chat.rocket.android.widget.emoji.ComposerEditText
import chat.rocket.android.widget.emoji.Emoji import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiFragment import chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
...@@ -52,7 +52,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" ...@@ -52,7 +52,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42 private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
@Inject lateinit var presenter: ChatRoomPresenter @Inject lateinit var presenter: ChatRoomPresenter
@Inject lateinit var parser: MessageParser @Inject lateinit var parser: MessageParser
private lateinit var adapter: ChatRoomAdapter private lateinit var adapter: ChatRoomAdapter
...@@ -60,6 +60,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -60,6 +60,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var chatRoomName: String private lateinit var chatRoomName: String
private lateinit var chatRoomType: String private lateinit var chatRoomType: String
private lateinit var emojiKeyboardPopup: EmojiKeyboardPopup
private var isChatRoomReadOnly: Boolean = false private var isChatRoomReadOnly: Boolean = false
private lateinit var actionSnackbar: ActionSnackbar private lateinit var actionSnackbar: ActionSnackbar
...@@ -106,8 +107,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -106,8 +107,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
attachOrGetEmojiFragment() text_message.addTextChangedListener(EmojiKeyboardPopup.EmojiTextWatcher(text_message))
text_message.addTextChangedListener(EmojiFragment.EmojiTextWatcher(text_message))
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -212,10 +212,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -212,10 +212,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
adapter.removeItem(msgId) adapter.removeItem(msgId)
} }
override fun restoreUIState() {
hideAllKeyboards()
}
override fun showReplyingAction(username: String, replyMarkdown: String, quotedMessage: String) { override fun showReplyingAction(username: String, replyMarkdown: String, quotedMessage: String) {
activity?.apply { activity?.apply {
citation = replyMarkdown citation = replyMarkdown
...@@ -269,27 +265,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -269,27 +265,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
} }
} }
override fun onEmojiPanelCollapsed() {
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
}
override fun onEmojiPanelExpanded() {
}
private fun setReactionButtonIcon(@DrawableRes drawableId: Int) { private fun setReactionButtonIcon(@DrawableRes drawableId: Int) {
button_add_reaction.setImageResource(drawableId) button_add_reaction.setImageResource(drawableId)
button_add_reaction.setTag(drawableId) button_add_reaction.setTag(drawableId)
} }
private fun hideAllKeyboards() {
activity?.let {
KeyboardHelper.hideSoftKeyboard(it)
attachOrGetEmojiFragment()?.collapse()
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
}
}
override fun showFileSelection(filter: Array<String>) { override fun showFileSelection(filter: Array<String>) {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*" intent.type = "*/*"
...@@ -329,28 +309,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -329,28 +309,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
input_container.setVisible(false) input_container.setVisible(false)
} else { } else {
subscribeTextMessage() subscribeTextMessage()
emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this
text_message.listener = object : ComposerEditText.ComposerEditTextListener { text_message.listener = object : ComposerEditText.ComposerEditTextListener {
override fun onKeyboardOpened() { override fun onKeyboardOpened() {
activity?.let {
val fragment = EmojiFragment.getOrAttach(it, R.id.emoji_fragment_placeholder, composer)
if (fragment.isCollapsed()) {
fragment.expandHidden()
}
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
}
} }
override fun onKeyboardClosed() { override fun onKeyboardClosed() {
activity?.let { activity?.let {
setReactionButtonIcon(R.drawable.ic_reaction_24dp) if (!emojiKeyboardPopup.isKeyboardOpen) {
val fragment = EmojiFragment.getOrAttach(it, R.id.emoji_fragment_placeholder, composer)
if (fragment.isCollapsed()) {
it.onBackPressed() it.onBackPressed()
} else {
hideAllKeyboards()
} }
KeyboardHelper.hideSoftKeyboard(it)
emojiKeyboardPopup.dismiss()
} }
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
} }
} }
...@@ -358,11 +331,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -358,11 +331,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
var textMessage = citation ?: "" var textMessage = citation ?: ""
textMessage += text_message.textContent textMessage += text_message.textContent
sendMessage(textMessage) sendMessage(textMessage)
attachOrGetEmojiFragment()?.let {
if (it.softKeyboardVisible) {
it.collapse()
}
}
clearMessageComposition() clearMessageComposition()
} }
...@@ -370,7 +339,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -370,7 +339,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
if (layout_message_attachment_options.isShown) { if (layout_message_attachment_options.isShown) {
hideAttachmentOptions() hideAttachmentOptions()
} else { } else {
hideAllKeyboards()
showAttachmentOptions() showAttachmentOptions()
} }
} }
...@@ -390,33 +358,29 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener { ...@@ -390,33 +358,29 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiFragment.Listener {
} }
button_add_reaction.setOnClickListener { view -> button_add_reaction.setOnClickListener { view ->
activity?.let { openEmojiKeyboardPopup()
val editor = text_message
val emojiFragment = attachOrGetEmojiFragment()!!
val tag = if (view.tag == null) R.drawable.ic_reaction_24dp else view.tag as Int
when (tag) {
R.drawable.ic_reaction_24dp -> {
KeyboardHelper.hideSoftKeyboard(it)
if (!emojiFragment.isExpanded()) {
emojiFragment.show()
}
setReactionButtonIcon(R.drawable.ic_keyboard_black_24dp)
}
R.drawable.ic_keyboard_black_24dp -> {
KeyboardHelper.showSoftKeyboard(editor)
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
}
}
}
} }
} }
} }
private fun attachOrGetEmojiFragment(): EmojiFragment? { private fun openEmojiKeyboardPopup() {
return activity?.let { if (!emojiKeyboardPopup.isShowing()) {
val frag = EmojiFragment.getOrAttach(it, R.id.emoji_fragment_placeholder, composer) // If keyboard is visible, simply show the popup
frag.listener = this if (emojiKeyboardPopup.isKeyboardOpen) {
frag emojiKeyboardPopup.showAtBottom()
} else {
// Open the text keyboard first and immediately after that show the emoji popup
text_message.setFocusableInTouchMode(true)
text_message.requestFocus()
emojiKeyboardPopup.showAtBottomPending()
KeyboardHelper.showSoftKeyboard(text_message)
}
setReactionButtonIcon(R.drawable.ic_keyboard_black_24dp)
} else {
// If popup is showing, simply dismiss it to show the undelying text keyboard
emojiKeyboardPopup.dismiss()
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
} }
} }
......
package chat.rocket.android.helper package chat.rocket.android.helper
import android.graphics.Rect
import android.view.View
import android.content.Context.INPUT_METHOD_SERVICE
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Rect
import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
...@@ -36,7 +35,7 @@ object KeyboardHelper { ...@@ -36,7 +35,7 @@ object KeyboardHelper {
val currentFocus = activity.currentFocus val currentFocus = activity.currentFocus
if (currentFocus != null) { if (currentFocus != null) {
val inputMethodManager = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager val inputMethodManager = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(currentFocus.windowToken, 0) inputMethodManager.hideSoftInputFromWindow(currentFocus.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
} }
} }
...@@ -48,14 +47,7 @@ object KeyboardHelper { ...@@ -48,14 +47,7 @@ object KeyboardHelper {
fun showSoftKeyboard(view: View) { fun showSoftKeyboard(view: View) {
if (view.requestFocus()) { if (view.requestFocus()) {
val inputMethodManager = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager val inputMethodManager = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0) inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.SHOW_IMPLICIT)
}
}
fun restart(view: View) {
if (view.requestFocus()) {
val inputMethodManager = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.restartInput(view)
} }
} }
} }
\ No newline at end of file
...@@ -9,9 +9,7 @@ import android.view.View ...@@ -9,9 +9,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.widget.emoji.EmojiFragment.Listener import chat.rocket.android.widget.emoji.EmojiKeyboardPopup.Listener
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import java.util.* import java.util.*
class CategoryPagerAdapter(val listener: Listener) : PagerAdapter() { class CategoryPagerAdapter(val listener: Listener) : PagerAdapter() {
......
package chat.rocket.android.widget.emoji package chat.rocket.android.widget.emoji
import android.graphics.Rect import android.content.Context
import android.os.Bundle
import android.support.annotation.IdRes
import android.support.design.widget.TabLayout import android.support.design.widget.TabLayout
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.view.ViewPager import android.support.v4.view.ViewPager
import android.support.v7.app.AppCompatActivity
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.* import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import chat.rocket.android.R import chat.rocket.android.R
class EmojiFragment : Fragment() { class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow(context, view) {
private lateinit var viewPager: ViewPager private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout private lateinit var tabLayout: TabLayout
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 var editor: View? = null
private var decorLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
var softKeyboardVisible = false
var listener: Listener? = null var listener: Listener? = null
companion object { companion object {
const val PREF_EMOJI_RECENTS = "PREF_EMOJI_RECENTS" const val PREF_EMOJI_RECENTS = "PREF_EMOJI_RECENTS"
const val PREF_KEYBOARD_HEIGHT = "PREF_KEYBOARD_HEIGHT"
const val MIN_KEYBOARD_HEIGHT_PX = 150
val TAG: String = EmojiFragment::class.java.simpleName
fun newInstance(editor: View) = EmojiFragment().apply { this.editor = editor }
fun getOrAttach(activity: FragmentActivity, @IdRes containerId: Int, editor: View): EmojiFragment {
val fragmentManager = activity.supportFragmentManager
var fragment: Fragment? = fragmentManager.findFragmentByTag(TAG)
return if (fragment == null) {
fragment = newInstance(editor)
fragment.parentContainer = activity.findViewById(containerId)
fragmentManager.beginTransaction()
.replace(containerId, fragment, TAG)
.commit()
fragment
} else {
fragment as EmojiFragment
}
}
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.emoji_popup_layout, container, false) val view = inflater.inflate(R.layout.emoji_popup_layout, null, false)
parentContainer = view.findViewById(R.id.emoji_keyboard_container) parentContainer = view.findViewById(R.id.emoji_keyboard_container)
viewPager = view.findViewById(R.id.pager_categories) viewPager = view.findViewById(R.id.pager_categories)
searchView = view.findViewById(R.id.emoji_search) searchView = view.findViewById(R.id.emoji_search)
...@@ -60,67 +38,13 @@ class EmojiFragment : Fragment() { ...@@ -60,67 +38,13 @@ class EmojiFragment : Fragment() {
return view return view
} }
override fun onDetach() { override fun onViewCreated(view: View) {
super.onDetach()
activity?.getWindow()?.decorView?.viewTreeObserver?.removeOnGlobalLayoutListener(decorLayoutListener)
listener = null
editor = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupLayout()
setupViewPager() setupViewPager()
setupBottomBar() setupBottomBar()
} }
private fun setupLayout() {
activity?.let {
val decorView = it.getWindow().decorView
decorLayoutListener = object : ViewTreeObserver.OnGlobalLayoutListener {
private val windowVisibleDisplayFrame = Rect()
private var lastVisibleDecorViewHeight: Int = 0
override fun onGlobalLayout() {
if (editor == null) {
return
}
// Retrieve visible rectangle inside window.
decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame)
val visibleDecorViewHeight = windowVisibleDisplayFrame.height()
// Decide whether keyboard is visible from changing decor view height.
if (lastVisibleDecorViewHeight != 0) {
if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
// Calculate current keyboard height (this includes also navigation bar height when in fullscreen mode).
val currentKeyboardHeight = decorView.height - windowVisibleDisplayFrame.bottom - editor!!.measuredHeight
// Notify listener about keyboard being shown.
EmojiRepository.saveKeyboardHeight(currentKeyboardHeight)
setKeyboardHeight(currentKeyboardHeight)
softKeyboardVisible = true
parentContainer.postDelayed({
expandHidden()
}, 100)
} else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
// Notify listener about keyboard being hidden.
softKeyboardVisible = false
}
}
// Save current decor view height for the next call.
lastVisibleDecorViewHeight = visibleDecorViewHeight
}
}
decorView.viewTreeObserver.addOnGlobalLayoutListener(decorLayoutListener)
}
val storedHeight = EmojiRepository.getKeyboardHeight()
if (storedHeight > 0) {
setKeyboardHeight(storedHeight)
}
}
private fun setupBottomBar() { private fun setupBottomBar() {
searchView.setOnClickListener { searchView.setOnClickListener {
} }
backspaceView.setOnClickListener { backspaceView.setOnClickListener {
...@@ -129,11 +53,11 @@ class EmojiFragment : Fragment() { ...@@ -129,11 +53,11 @@ class EmojiFragment : Fragment() {
} }
private fun setupViewPager() { private fun setupViewPager() {
activity?.let { context.let {
val callback = when (it) { val callback = when (it) {
is Listener -> it as Listener is Listener -> it
else -> { else -> {
val fragments = it.supportFragmentManager.fragments val fragments = (it as AppCompatActivity).supportFragmentManager.fragments
if (fragments == null || fragments.size == 0 || !(fragments[0] is Listener)) { if (fragments == null || fragments.size == 0 || !(fragments[0] is Listener)) {
throw IllegalStateException("activity/fragment should implement Listener interface") throw IllegalStateException("activity/fragment should implement Listener interface")
} }
...@@ -141,14 +65,6 @@ class EmojiFragment : Fragment() { ...@@ -141,14 +65,6 @@ class EmojiFragment : Fragment() {
} }
} }
viewPager.adapter = CategoryPagerAdapter(object : Listener { viewPager.adapter = CategoryPagerAdapter(object : Listener {
override fun onEmojiPanelExpanded() {
// do nothing
}
override fun onEmojiPanelCollapsed() {
// do nothing
}
override fun onNonEmojiKeyPressed(keyCode: Int) { override fun onNonEmojiKeyPressed(keyCode: Int) {
// do nothing // do nothing
} }
...@@ -161,7 +77,7 @@ class EmojiFragment : Fragment() { ...@@ -161,7 +77,7 @@ class EmojiFragment : Fragment() {
for (category in EmojiCategory.values()) { for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal) val tab = tabLayout.getTabAt(category.ordinal)
val tabView = layoutInflater.inflate(R.layout.emoji_picker_tab, null) val tabView = LayoutInflater.from(context).inflate(R.layout.emoji_picker_tab, null)
tab?.setCustomView(tabView) tab?.setCustomView(tabView)
val textView = tabView.findViewById(R.id.image_category) as ImageView val textView = tabView.findViewById(R.id.image_category) as ImageView
textView.setImageResource(category.resourceIcon()) textView.setImageResource(category.resourceIcon())
...@@ -173,14 +89,6 @@ class EmojiFragment : Fragment() { ...@@ -173,14 +89,6 @@ class EmojiFragment : Fragment() {
} }
} }
private fun setKeyboardHeight(height: Int) {
val oldHeight = parentContainer.layoutParams.height
if (oldHeight != height) {
parentContainer.layoutParams.height = height
parentContainer.requestLayout()
}
}
class EmojiTextWatcher(val editor: EditText) : TextWatcher { class EmojiTextWatcher(val editor: EditText) : TextWatcher {
@Volatile private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>() @Volatile private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
...@@ -225,51 +133,6 @@ class EmojiFragment : Fragment() { ...@@ -225,51 +133,6 @@ class EmojiFragment : Fragment() {
} }
} }
private fun setKeyboardVisibility(visibility: Int) {
if (visibility != parentContainer.visibility) {
parentContainer.visibility = visibility
}
}
/**
* Show the emoji keyboard.
*/
fun show() {
setKeyboardVisibility(View.VISIBLE)
}
/**
* Expand the emoji keyboard with invisible contents.
*/
fun expandHidden() {
setKeyboardVisibility(View.INVISIBLE)
}
/**
* Hide the emoji keyboard.
*/
fun collapse() {
// Since the emoji keyboard is always behind the soft keyboard assume it's also dismissed
// when the emoji one is about to get close. Hence we should invoke our listener to update
// the UI as if the soft keyboard is hidden.
listener?.onEmojiPanelCollapsed()
setKeyboardVisibility(View.GONE)
}
/**
* Whether the emoji keyboard is visible.
*
* @return <code>true</code> if opened.
*/
fun isExpanded() = parentContainer.visibility == View.VISIBLE
/**
* Whether the emoji keyboard is collapsed.
*
* @return false if the emoji keyboard is visible and not obscured
*/
fun isCollapsed() = parentContainer.visibility == View.GONE
interface Listener { interface Listener {
/** /**
* When an emoji is selected on the picker. * When an emoji is selected on the picker.
...@@ -286,9 +149,5 @@ class EmojiFragment : Fragment() { ...@@ -286,9 +149,5 @@ class EmojiFragment : Fragment() {
* @see android.view.KeyEvent * @see android.view.KeyEvent
*/ */
fun onNonEmojiKeyPressed(keyCode: Int) fun onNonEmojiKeyPressed(keyCode: Int)
fun onEmojiPanelCollapsed()
fun onEmojiPanelExpanded()
} }
} }
\ No newline at end of file
...@@ -79,14 +79,14 @@ object EmojiRepository { ...@@ -79,14 +79,14 @@ object EmojiRepository {
*/ */
fun addToRecents(emoji: Emoji) { fun addToRecents(emoji: Emoji) {
val emojiShortname = emoji.shortname val emojiShortname = emoji.shortname
val recentsJson = JSONObject(preferences.getString(EmojiFragment.PREF_EMOJI_RECENTS, "{}")) val recentsJson = JSONObject(preferences.getString(EmojiKeyboardPopup.PREF_EMOJI_RECENTS, "{}"))
if (recentsJson.has(emojiShortname)) { if (recentsJson.has(emojiShortname)) {
val useCount = recentsJson.getInt(emojiShortname) val useCount = recentsJson.getInt(emojiShortname)
recentsJson.put(emojiShortname, useCount + 1) recentsJson.put(emojiShortname, useCount + 1)
} else { } else {
recentsJson.put(emojiShortname, 1) recentsJson.put(emojiShortname, 1)
} }
preferences.edit().putString(EmojiFragment.PREF_EMOJI_RECENTS, recentsJson.toString()).apply() preferences.edit().putString(EmojiKeyboardPopup.PREF_EMOJI_RECENTS, recentsJson.toString()).apply()
} }
/** /**
...@@ -96,7 +96,7 @@ object EmojiRepository { ...@@ -96,7 +96,7 @@ object EmojiRepository {
*/ */
fun getRecents(): List<Emoji> { fun getRecents(): List<Emoji> {
val list = mutableListOf<Emoji>() val list = mutableListOf<Emoji>()
val recentsJson = JSONObject(preferences.getString(EmojiFragment.PREF_EMOJI_RECENTS, "{}")) val recentsJson = JSONObject(preferences.getString(EmojiKeyboardPopup.PREF_EMOJI_RECENTS, "{}"))
for (shortname in recentsJson.keys()) { for (shortname in recentsJson.keys()) {
val emoji = getEmojiByShortname(shortname) val emoji = getEmojiByShortname(shortname)
emoji?.let { emoji?.let {
...@@ -110,25 +110,6 @@ object EmojiRepository { ...@@ -110,25 +110,6 @@ object EmojiRepository {
return list return list
} }
/**
* Store current soft keyboard height for later reference.
*/
fun saveKeyboardHeight(height: Int) {
if (height <= 0) {
return
}
preferences.edit()
.putInt(EmojiFragment.PREF_KEYBOARD_HEIGHT, height)
.apply()
}
/**
* Get stored keyboard height.
*
* @return Height of the current soft keyboard.
*/
fun getKeyboardHeight() = preferences.getInt(EmojiFragment.PREF_KEYBOARD_HEIGHT, 0)
/** /**
* Replace shortnames to unicode characters. * Replace shortnames to unicode characters.
*/ */
......
/**
* Copyright 2015 YA LLC
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package chat.rocket.android.widget.emoji
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.Build
import android.view.*
import android.widget.PopupWindow
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
/**
* Base class to create popup window that appears over software keyboard.
*/
abstract class OverKeyboardPopupWindow(val context: Context, private val rootView: View) : PopupWindow(context), ViewTreeObserver.OnGlobalLayoutListener {
/**
* @return keyboard height in pixels
*/
var keyboardHeight = 0
private set
private var pendingOpen = false
/**
* @return Returns true if the soft keyboard is open, false otherwise.
*/
var isKeyboardOpen = false
private set
private var keyboardHideListener: OnKeyboardHideListener? = null
interface OnKeyboardHideListener {
fun onKeyboardHide()
}
init {
setBackgroundDrawable(null)
if (BuildConfig.VERSION_CODE >= Build.VERSION_CODES.LOLLIPOP) {
elevation = 0f
}
val view = onCreateView(LayoutInflater.from(context))
onViewCreated(view)
contentView = view
softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
// Default size
setSize(this.context.resources.getDimensionPixelSize(R.dimen.supposed_keyboard_height),
WindowManager.LayoutParams.MATCH_PARENT)
setSizeForSoftKeyboard()
}
fun setKeyboardHideListener(keyboardHideListener: OnKeyboardHideListener) {
this.keyboardHideListener = keyboardHideListener
}
/**
* Manually set the popup window size
*
* @param width Width of the popup
* @param height Height of the popup
*/
fun setSize(width: Int, height: Int) {
setWidth(width)
setHeight(height)
}
/**
* Call this function to resize the emoji popup according to your soft keyboard size
*/
fun setSizeForSoftKeyboard() {
val viewTreeObserver = rootView.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(this)
}
override fun onGlobalLayout() {
val r = Rect()
rootView.getWindowVisibleDisplayFrame(r)
val screenHeight = calculateScreenHeight()
var heightDifference = screenHeight - (r.bottom - r.top)
val resources = context.resources
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
heightDifference -= resources.getDimensionPixelSize(resourceId)
}
if (heightDifference > 100) {
keyboardHeight = heightDifference
setSize(WindowManager.LayoutParams.MATCH_PARENT, keyboardHeight)
isKeyboardOpen = true
if (pendingOpen) {
showAtBottom()
pendingOpen = false
}
} else {
if (isKeyboardOpen && keyboardHideListener != null) {
keyboardHideListener!!.onKeyboardHide()
}
isKeyboardOpen = false
}
}
private fun calculateScreenHeight(): Int {
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val display = wm.getDefaultDisplay()
val size = Point()
display.getSize(size)
return size.y
}
/**
* Use this function to show the popup.
* NOTE: Since, the soft keyboard sizes are variable on different android devices, the
* library needs you to open the soft keyboard at least once before calling this function.
* If that is not possible see showAtBottomPending() function.
*/
fun showAtBottom() {
showAtLocation(rootView, Gravity.BOTTOM, 0, 0)
}
/**
* Use this function when the soft keyboard has not been opened yet. This
* will show the popup after the keyboard is up next time.
* Generally, you will be calling InputMethodManager.showSoftInput function after
* calling this function.
*/
fun showAtBottomPending() {
if (isKeyboardOpen) {
showAtBottom()
} else {
pendingOpen = true
}
}
abstract fun onCreateView(inflater: LayoutInflater): View
abstract fun onViewCreated(view: View)
}
\ No newline at end of file
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
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="200dp" android:layout_height="0dp"
android:background="@color/white" android:background="@color/white">
android:visibility="gone">
<View <View
android:id="@+id/divider" android:id="@+id/divider"
......
...@@ -17,4 +17,6 @@ ...@@ -17,4 +17,6 @@
<!-- Emoji --> <!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen> <dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen>
</resources> </resources>
\ No newline at end of file
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