Commit 3c7503e1 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.Android into new/star-unstar-chatroom

parents 2647a81c 847d6e6f
......@@ -10,7 +10,7 @@ android {
defaultConfig {
applicationId "chat.rocket.android"
minSdkVersion 21
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 2030
versionName "2.4.0"
......@@ -61,6 +61,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player')
implementation project(':emoji')
implementation libraries.kotlin
implementation libraries.coroutines
......
......@@ -19,7 +19,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.android.emoji.EmojiRepository
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig
......
package chat.rocket.android.chatroom.adapter
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.AudioAttachmentUiModel
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.*
class AudioAttachmentViewHolder(itemView: View,
......@@ -15,8 +15,8 @@ class AudioAttachmentViewHolder(itemView: View,
init {
with(itemView) {
setupActionMenu(attachment_container)
image_attachment.setVisible(false)
audio_video_attachment.setVisible(true)
image_attachment.isVisible = false
audio_video_attachment.isVisible = true
}
}
......
......@@ -6,8 +6,8 @@ import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.AuthorAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.common.util.ifNull
import kotlinx.android.synthetic.main.item_author_attachment.view.*
......
......@@ -9,8 +9,8 @@ import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import com.google.android.flexbox.FlexDirection
......
......@@ -9,7 +9,7 @@ import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.uimodel.*
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import timber.log.Timber
......
......@@ -6,7 +6,7 @@ import android.text.method.LinkMovementMethod
import android.view.View
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.ColorAttachmentUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_color_attachment.view.*
......@@ -16,7 +16,7 @@ class ColorAttachmentViewHolder(itemView: View,
: BaseViewHolder<ColorAttachmentUiModel>(itemView, listener, reactionListener) {
val drawable: Drawable? = ContextCompat.getDrawable(itemView.context,
R.drawable.quote_vertical_bar)
R.drawable.quote_vertical_gray_bar)
init {
with(itemView) {
......
......@@ -4,8 +4,8 @@ import android.content.Intent
import android.view.View
import androidx.core.net.toUri
import chat.rocket.android.chatroom.uimodel.GenericFileAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_file_attachment.view.*
class GenericFileAttachmentViewHolder(itemView: View,
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.chatroom.adapter
import android.view.View
import chat.rocket.android.chatroom.uimodel.ImageAttachmentUiModel
import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import com.facebook.drawee.backends.pipeline.Fresco
import kotlinx.android.synthetic.main.message_attachment.view.*
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.chatroom.adapter
import android.text.method.LinkMovementMethod
import android.view.View
import chat.rocket.android.chatroom.uimodel.MessageAttachmentUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_attachment.view.*
class MessageAttachmentViewHolder(
......
package chat.rocket.android.chatroom.adapter
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.ReactionUiModel
import chat.rocket.android.dagger.DaggerLocalComponent
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiListenerAdapter
import chat.rocket.android.widget.emoji.EmojiPickerPopup
import chat.rocket.android.widget.emoji.EmojiReactionListener
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject
......@@ -72,21 +72,23 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
}
fun contains(reactionShortname: String) =
reactions.firstOrNull { it.shortname == reactionShortname} != null
reactions.firstOrNull { it.shortname == reactionShortname } != null
class SingleReactionViewHolder(view: View,
private val listener: EmojiReactionListener?)
: RecyclerView.ViewHolder(view), View.OnClickListener {
@Inject lateinit var localRepository: LocalRepository
@Volatile lateinit var reaction: ReactionUiModel
@Inject
lateinit var localRepository: LocalRepository
@Volatile
lateinit var reaction: ReactionUiModel
@Volatile
var clickHandled = false
init {
DaggerLocalComponent.builder()
.context(itemView.context)
.build()
.inject(this)
.context(itemView.context)
.build()
.inject(this)
}
fun bind(reaction: ReactionUiModel) {
......@@ -125,7 +127,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
itemView as ImageView
itemView.setOnClickListener {
val emojiPickerPopup = EmojiPickerPopup(itemView.context)
emojiPickerPopup.listener = object : EmojiListenerAdapter() {
emojiPickerPopup.listener = object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
listener?.onReactionAdded(messageId, emoji)
}
......
......@@ -2,7 +2,7 @@ package chat.rocket.android.chatroom.adapter
import android.view.View
import chat.rocket.android.chatroom.uimodel.MessageReplyUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_reply.view.*
class MessageReplyViewHolder(
......
......@@ -5,7 +5,7 @@ import android.text.method.LinkMovementMethod
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.core.model.isSystemMessage
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.*
......
......@@ -2,11 +2,11 @@ package chat.rocket.android.chatroom.adapter
import android.net.Uri
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content
import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View,
......@@ -23,10 +23,10 @@ class UrlPreviewViewHolder(itemView: View,
override fun bindViews(data: UrlPreviewUiModel) {
with(itemView) {
if (data.thumbUrl.isNullOrEmpty()) {
image_preview.setVisible(false)
image_preview.isVisible = false
} else {
image_preview.setImageURI(data.thumbUrl)
image_preview.setVisible(true)
image_preview.isVisible = true
}
text_host.content = data.hostname
text_title.content = data.title
......
package chat.rocket.android.chatroom.adapter
import android.view.View
import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.VideoAttachmentUiModel
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.*
class VideoAttachmentViewHolder(itemView: View,
......@@ -15,8 +15,8 @@ class VideoAttachmentViewHolder(itemView: View,
init {
with(itemView) {
setupActionMenu(attachment_container)
image_attachment.setVisible(false)
audio_video_attachment.setVisible(true)
image_attachment.isVisible = false
audio_video_attachment.isVisible = true
}
}
......
......@@ -16,7 +16,18 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.ChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.RoomRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.domain.uploadMaxFileSize
import chat.rocket.android.server.domain.uploadMimeTypeFilter
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extensions.avatarUrl
......@@ -33,11 +44,27 @@ import chat.rocket.core.internal.realtime.setTypingStatus
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.subscribeTypingStatus
import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.rest.*
import chat.rocket.core.internal.rest.chatRoomRoles
import chat.rocket.core.internal.rest.commands
import chat.rocket.core.internal.rest.deleteMessage
import chat.rocket.core.internal.rest.getMembers
import chat.rocket.core.internal.rest.history
import chat.rocket.core.internal.rest.joinChat
import chat.rocket.core.internal.rest.markAsRead
import chat.rocket.core.internal.rest.messages
import chat.rocket.core.internal.rest.pinMessage
import chat.rocket.core.internal.rest.runCommand
import chat.rocket.core.internal.rest.sendMessage
import chat.rocket.core.internal.rest.spotlight
import chat.rocket.core.internal.rest.starMessage
import chat.rocket.core.internal.rest.toggleReaction
import chat.rocket.core.internal.rest.unpinMessage
import chat.rocket.core.internal.rest.unstarMessage
import chat.rocket.core.internal.rest.updateMessage
import chat.rocket.core.internal.rest.uploadFile
import chat.rocket.core.model.ChatRoomRole
import chat.rocket.core.model.Command
import chat.rocket.core.model.Message
import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.android.UI
......@@ -139,12 +166,10 @@ class ChatRoomPresenter @Inject constructor(
try {
if (offset == 0L) {
val localMessages = messagesRepository.getByRoomId(chatRoomId)
val oldMessages = mapper.map(
localMessages, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast, isRoom = true
)
)
val oldMessages = mapper.map(localMessages, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast, isRoom = true
))
if (oldMessages.isNotEmpty()) {
view.showMessages(oldMessages)
loadMissingMessages()
......@@ -187,12 +212,10 @@ class ChatRoomPresenter @Inject constructor(
}
messagesRepository.saveAll(messages)
view.showMessages(
mapper.map(
messages, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast, isRoom = true
)
)
mapper.map(messages, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast, isRoom = true
))
)
}
......@@ -231,8 +254,8 @@ class ChatRoomPresenter @Inject constructor(
view.showNewMessage(
mapper.map(
newMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast
)
roles = chatRoles, isBroadcast = chatIsBroadcast
)
)
)
client.sendMessage(id, chatRoomId, text)
......@@ -415,13 +438,11 @@ class ChatRoomPresenter @Inject constructor(
Timber.d("History: $messages")
if (messages.result.isNotEmpty()) {
val models = mapper.map(
messages.result, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast,
isRoom = true
)
)
val models = mapper.map(messages.result, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast,
isRoom = true
))
messagesRepository.saveAll(messages.result)
launchUI(strategy) {
......@@ -481,18 +502,12 @@ class ChatRoomPresenter @Inject constructor(
fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean) {
launchUI(strategy) {
val message = messagesRepository.getById(messageId)
val me: Myself? = try {
retryIO("me()") { client.me() } //TODO: Cache this and use an interactor
} catch (ex: Exception) {
Timber.e(ex)
null
}
val currentUsername: String? = userHelper.user()?.username
message?.let { msg ->
val id = msg.id
val username = msg.sender?.username ?: ""
val mention = if (mentionAuthor && me?.username != username) "@$username" else ""
val room =
if (roomTypeOf(roomType) is RoomType.DirectMessage) username else roomName
val mention = if (mentionAuthor && currentUsername != username) "@$username" else ""
val room = if (roomTypeOf(roomType) is RoomType.DirectMessage) username else roomName
val chatRoomType = when (roomTypeOf(roomType)) {
is RoomType.DirectMessage -> "direct"
is RoomType.PrivateGroup -> "group"
......@@ -503,12 +518,10 @@ class ChatRoomPresenter @Inject constructor(
view.showReplyingAction(
username = getDisplayName(msg.sender),
replyMarkdown = "[ ]($currentServer/$chatRoomType/$room?msg=$id) $mention ",
quotedMessage = mapper.map(
message, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast
)
).last().preview?.message ?: ""
quotedMessage = mapper.map(message, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast
)).last().preview?.message ?: ""
)
}
}
......@@ -930,11 +943,9 @@ class ChatRoomPresenter @Inject constructor(
private fun updateMessage(streamedMessage: Message) {
launchUI(strategy) {
val viewModelStreamedMessage = mapper.map(
streamedMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast
)
)
val viewModelStreamedMessage = mapper.map(streamedMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast
))
val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId)
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
......
package chat.rocket.android.chatroom.ui
import com.google.android.material.snackbar.BaseTransientBottomBar
import androidx.core.view.ViewCompat
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.setPadding
import chat.rocket.android.R
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.content
import com.google.android.material.snackbar.BaseTransientBottomBar
import kotlinx.android.synthetic.main.message_action_bar.view.*
import ru.noties.markwon.Markwon
class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
class ActionSnackbar private constructor(
parentViewGroup: ViewGroup, content:
View, contentViewCallback: com.google.android.material.snackbar.ContentViewCallback
) : BaseTransientBottomBar<ActionSnackbar>(parentViewGroup, content, contentViewCallback) {
companion object {
fun make(parentViewGroup: ViewGroup, content: String = "", parser: MessageParser): ActionSnackbar {
val context = parentViewGroup.context
val view = LayoutInflater.from(context).inflate(R.layout.message_action_bar, parentViewGroup, false)
val actionSnackbar = ActionSnackbar(parentViewGroup, view, CallbackImpl(view))
actionSnackbar.parser = parser
actionSnackbar.messageTextView = view.findViewById(R.id.text_view_action_text) as TextView
actionSnackbar.titleTextView = view.findViewById(R.id.text_view_action_title) as TextView
actionSnackbar.cancelView = view.findViewById(R.id.image_view_action_cancel_quote) as ImageView
actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE
val spannable = Markwon.markdown(context, content).trim()
actionSnackbar.messageTextView.content = spannable
with(view) {
actionSnackbar.getView().setPadding(0)
actionSnackbar.getView().setBackgroundColor(ContextCompat.getColor(context, R.color.colorWhite))
actionSnackbar.parser = parser
actionSnackbar.messageTextView = text_view_action_text
actionSnackbar.titleTextView = text_view_action_title
actionSnackbar.cancelView = image_view_action_cancel_quote
actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE
val spannable = Markwon.markdown(context, content).trim()
actionSnackbar.messageTextView.content = spannable
}
return actionSnackbar
}
}
......@@ -56,25 +65,22 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
title = ""
}
private constructor(parentViewGroup: ViewGroup, content: View, contentViewCallback: BaseTransientBottomBar.ContentViewCallback) :
super(parentViewGroup, content, contentViewCallback)
class CallbackImpl(val content: View) : BaseTransientBottomBar.ContentViewCallback {
class CallbackImpl(val content: View) : com.google.android.material.snackbar.ContentViewCallback {
override fun animateContentOut(delay: Int, duration: Int) {
ViewCompat.setScaleY(content, 1f)
content.scaleY = 1f
ViewCompat.animate(content)
.scaleY(0f)
.setDuration(duration.toLong())
.startDelay = delay.toLong()
.scaleY(0f)
.setDuration(duration.toLong())
.startDelay = delay.toLong()
}
override fun animateContentIn(delay: Int, duration: Int) {
ViewCompat.setScaleY(content, 0f)
content.scaleY = 0f
ViewCompat.animate(content)
.scaleY(1f)
.setDuration(duration.toLong())
.startDelay = delay.toLong()
.scaleY(1f)
.setDuration(duration.toLong())
.startDelay = delay.toLong()
}
}
}
\ No newline at end of file
......@@ -8,17 +8,27 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.text.SpannableStringBuilder
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.core.text.bold
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.text.SpannableStringBuilder
import android.view.*
import androidx.core.text.bold
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.*
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.uimodel.BaseUiModel
......@@ -26,6 +36,13 @@ import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.emoji.ComposerEditText
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiKeyboardPopup
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser
......@@ -39,7 +56,6 @@ import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.emoji.*
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State
......@@ -618,7 +634,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showReactionsPopup(messageId: String) {
ui {
val emojiPickerPopup = EmojiPickerPopup(it)
emojiPickerPopup.listener = object : EmojiListenerAdapter() {
emojiPickerPopup.listener = object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
onReactionAdded(messageId, emoji)
}
......@@ -662,15 +678,15 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
handler.postDelayed(dismissStatus, 2000)
}
is State.Disconnected -> connection_status_text.text =
getString(R.string.status_disconnected)
getString(R.string.status_disconnected)
is State.Connecting -> connection_status_text.text =
getString(R.string.status_connecting)
getString(R.string.status_connecting)
is State.Authenticating -> connection_status_text.text =
getString(R.string.status_authenticating)
getString(R.string.status_authenticating)
is State.Disconnecting -> connection_status_text.text =
getString(R.string.status_disconnecting)
getString(R.string.status_disconnecting)
is State.Waiting -> connection_status_text.text =
getString(R.string.status_waiting, state.seconds)
getString(R.string.status_waiting, state.seconds)
}
}
}
......@@ -726,7 +742,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
subscribeComposeTextMessage()
emojiKeyboardPopup =
EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this
text_message.listener = object : ComposerEditText.ComposerEditTextListener {
override fun onKeyboardOpened() {
......
......@@ -26,7 +26,7 @@ import chat.rocket.android.server.domain.baseUrl
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.isNotNullNorEmpty
import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.core.model.ChatRoom
import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType
......
......@@ -13,6 +13,7 @@ import android.widget.CheckBox
import android.widget.RadioGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
......@@ -29,12 +30,11 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.model.ChatRoom
......@@ -242,16 +242,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override suspend fun updateChatRooms(newDataSet: List<ChatRoom>) {}
override fun showNoChatRoomsToDisplay() {
ui { text_no_data_to_display.setVisible(true) }
ui { text_no_data_to_display.isVisible = true }
}
override fun showLoading() {
ui { view_loading.setVisible(true) }
ui { view_loading.isVisible = true }
}
override fun hideLoading() {
ui {
view_loading.setVisible(false)
view_loading.isVisible = false
}
}
......
package chat.rocket.android.createchannel.ui
import android.os.Bundle
import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.createchannel.presentation.CreateChannelPresenter
import chat.rocket.android.createchannel.presentation.CreateChannelView
......@@ -18,7 +24,6 @@ import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import com.google.android.material.chip.Chip
......@@ -181,7 +186,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun setupToolBar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_create_channel)
getString(R.string.title_create_channel)
}
private fun setupViewListeners() {
......@@ -189,7 +194,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
if (isChecked) {
text_channel_type.text = getString(R.string.msg_private_channel)
text_channel_type_description.text =
getString(R.string.msg_private_channel_description)
getString(R.string.msg_private_channel_description)
image_channel_icon.setImageDrawable(
context?.getDrawable(R.drawable.ic_lock_black_12_dp)
)
......@@ -197,7 +202,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
} else {
text_channel_type.text = getString(R.string.msg_public_channel)
text_channel_type_description.text =
getString(R.string.msg_public_channel_description)
getString(R.string.msg_public_channel_description)
image_channel_icon.setImageDrawable(
context?.getDrawable(R.drawable.ic_hashtag_black_12dp)
)
......@@ -213,8 +218,8 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it))
LinearLayoutManager(context, RecyclerView.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL))
recycler_view.adapter = adapter
}
......
......@@ -2,7 +2,6 @@ package chat.rocket.android.db
import android.app.Application
import chat.rocket.android.db.model.BaseUserEntity
import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.db.model.UserStatus
......
......@@ -8,6 +8,8 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.DividerItemDecoration.HORIZONTAL
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
......@@ -22,7 +24,6 @@ import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_files.*
import javax.inject.Inject
......@@ -137,7 +138,7 @@ class FilesFragment : Fragment(), FilesView {
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager = linearLayoutManager
recycler_view.addItemDecoration(DividerItemDecoration(it))
recycler_view.addItemDecoration(DividerItemDecoration(it, HORIZONTAL))
recycler_view.adapter = adapter
}
}
......
......@@ -16,9 +16,9 @@ import chat.rocket.android.R
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.android.widget.emoji.EmojiTypefaceSpan
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.emoji.EmojiTypefaceSpan
import chat.rocket.common.model.SimpleUser
import chat.rocket.core.model.Message
import org.commonmark.node.AbstractVisitor
......
......@@ -5,9 +5,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.textContent
import kotlinx.android.synthetic.main.fragment_member_bottom_sheet.*
......@@ -70,15 +70,15 @@ class MemberBottomSheetFragment: BottomSheetDialogFragment() {
if (email.isNotEmpty()) {
text_member_email_address.textContent = email
} else {
text_email_address.setVisible(false)
text_member_email_address.setVisible(false)
text_email_address.isVisible = false
text_member_email_address.isVisible = false
}
if (utcOffset.isNotEmpty()){
text_member_utc.content = utcOffset
} else {
text_utc.setVisible(false)
text_member_utc.setVisible(false)
text_utc.isVisible = false
text_member_utc.isVisible = false
}
}
}
\ No newline at end of file
......@@ -7,6 +7,8 @@ import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
......@@ -15,10 +17,8 @@ import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject
......@@ -89,11 +89,11 @@ class MembersFragment : Fragment(), MembersView {
}
override fun showLoading() {
ui { view_loading.setVisible(true) }
ui { view_loading.isVisible = true }
}
override fun hideLoading() {
ui { view_loading.setVisible(false) }
ui { view_loading.isVisible = false }
}
override fun showMessage(resId: Int) {
......@@ -113,7 +113,7 @@ class MembersFragment : Fragment(), MembersView {
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager = linearLayoutManager
recycler_view.addItemDecoration(DividerItemDecoration(it))
recycler_view.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL))
recycler_view.adapter = adapter
}
}
......
......@@ -7,10 +7,10 @@ import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.settings.password.presentation.PasswordPresenter
import chat.rocket.android.settings.password.presentation.PasswordView
import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.textContent
import androidx.appcompat.view.ActionMode
import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
......
......@@ -7,8 +7,8 @@ import android.util.Base64
import android.util.Patterns
import android.widget.EditText
import android.widget.TextView
import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.android.widget.emoji.EmojiTypefaceSpan
import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiTypefaceSpan
import org.json.JSONObject
import ru.noties.markwon.Markwon
import java.net.URLDecoder
......
package chat.rocket.android.widget.emoji
data class Emoji(
val shortname: String,
val shortnameAlternates: List<String>,
val unicode: String,
val keywords: List<String>,
val category: String,
val count: Int = 0
)
\ No newline at end of file
package chat.rocket.android.widget.emoji
abstract class EmojiListenerAdapter : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
// this space is for rent
}
override fun onNonEmojiKeyPressed(keyCode: Int) {
// this space is for rent
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/actionMenuColor"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorAccent" />
<size
android:width="4dp"
android:height="4dp" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/quoteBar" />
<size
android:width="4dp"
android:height="4dp" />
<corners android:radius="8dp" />
</shape>
\ No newline at end of file
......@@ -18,7 +18,7 @@
android:layout_width="4dp"
android:layout_height="0dp"
android:layout_marginStart="56dp"
android:background="@drawable/quote_vertical_bar"
android:background="@drawable/quote_vertical_gray_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/recycler_view_reactions"/>
......
......@@ -18,7 +18,7 @@
android:layout_width="4dp"
android:layout_height="0dp"
android:layout_marginStart="56dp"
android:background="@drawable/quote_vertical_bar"
android:background="@drawable/quote_vertical_gray_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/recycler_view_reactions"/>
......
......@@ -18,7 +18,7 @@
android:layout_width="4dp"
android:layout_height="0dp"
android:layout_marginStart="56dp"
android:background="@drawable/quote_vertical_bar"
android:background="@drawable/quote_vertical_gray_bar"
app:layout_constraintBottom_toTopOf="@+id/recycler_view_reactions"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
......
......@@ -4,15 +4,16 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary">
android:background="@color/colorWhite">
<View
android:id="@+id/quote_bar"
android:layout_width="4dp"
android:layout_height="0dp"
android:background="@drawable/quote_vertical_bar"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginStart="8dp"
android:background="@drawable/quote_vertical_blue_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_view_action_cancel_quote"
app:layout_constraintTop_toTopOf="parent" />
......@@ -21,17 +22,17 @@
android:id="@+id/text_view_action_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="@color/colorWhite"
android:textColor="@color/actionMenuColor"
android:textSize="14sp"
android:typeface="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_view_action_cancel_quote"
app:layout_constraintStart_toEndOf="@+id/quote_bar"
app:layout_constraintTop_toBottomOf="@+id/text_view_action_title"
tools:text="action text" />
......@@ -39,26 +40,29 @@
android:id="@+id/image_view_action_cancel_quote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:adjustViewBounds="true"
android:cropToPadding="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/quote_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_close_white_24dp" />
app:srcCompat="@drawable/ic_close_gray_24dp" />
<TextView
android:id="@+id/text_view_action_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textColor="@color/colorAccent"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/text_view_action_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_view_action_cancel_quote"
app:layout_constraintStart_toEndOf="@+id/quote_bar"
app:layout_constraintTop_toTopOf="parent"
tools:text="Edit message" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -61,7 +61,7 @@
android:contentDescription="@string/msg_content_description_show_attachment_options"
android:src="@drawable/ic_reaction_24dp" />
<chat.rocket.android.widget.emoji.ComposerEditText
<chat.rocket.android.emoji.ComposerEditText
android:id="@+id/text_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
......
......@@ -17,7 +17,7 @@
android:layout_margin="16dp"
android:src="@drawable/ic_arrow_downward_24dp"
android:theme="@style/Theme.AppCompat"
android:tint="@color/gray_material"
android:tint="@color/actionMenuColor"
android:visibility="invisible"
app:backgroundTint="@color/colorWhite"
app:fabSize="mini"
......
......@@ -32,7 +32,7 @@
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:textColor="@color/gray_material"
android:textColor="@color/actionMenuColor"
android:textSize="14sp"
tools:text="Leave a channel" />
......
......@@ -46,7 +46,7 @@
android:layout_marginStart="5dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/gray_material"
android:textColor="@color/actionMenuColor"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_username"
......
......@@ -25,7 +25,7 @@
android:layout_toEndOf="@+id/text_name"
android:maxLines="1"
android:layout_marginStart="8dp"
android:textColor="@color/gray_material"
android:textColor="@color/actionMenuColor"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
......
......@@ -6,13 +6,13 @@
android:id="@+id/action_search"
android:icon="@drawable/ic_search_white_24px"
android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
android:actionViewClass="androidx.appcompat.widget.SearchView"
android:showAsAction="ifRoom|collapseActionView" />
<item
android:id="@+id/action_sort"
android:icon="@drawable/ic_sort"
android:title="@string/menu_chatroom_sort"
app:showAsAction="always" />
android:showAsAction="always" />
</menu>
......@@ -6,5 +6,5 @@
android:id="@+id/action_save_image"
android:icon="@drawable/ic_file_download_white_24dp"
android:title="@string/action_save_to_gallery"
app:showAsAction="always" />
android:showAsAction="always" />
</menu>
\ No newline at end of file
......@@ -238,7 +238,7 @@
<string name="Slash_Topic_Description">Set topic</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">No recent emoji</string>
<string name="msg_no_recent_emoji">No recent emojis</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">Sort</string>
......
......@@ -3,6 +3,7 @@ ext {
java : JavaVersion.VERSION_1_8,
compileSdk : 28,
targetSdk : 28,
minSdk : 21,
buildTools : '28.0.0-rc2',
kotlin : '1.2.50',
coroutine : '0.23.1',
......
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 1
versionName "0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation libraries.androidKtx
implementation libraries.appCompat
implementation libraries.kotlin
implementation libraries.constraintlayout
implementation libraries.recyclerview
implementation libraries.material
}
kotlin {
experimental {
coroutines "enable"
}
}
androidExtensions {
experimental = true
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android.emoji" />
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import androidx.viewpager.widget.PagerAdapter
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.util.extensions.setVisible
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.PagerAdapter
import kotlinx.android.synthetic.main.emoji_category_layout.view.*
import java.util.*
class CategoryPagerAdapter(val listener: EmojiKeyboardListener) : PagerAdapter() {
internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener) : PagerAdapter() {
override fun isViewFromObject(view: View, obj: Any): Boolean {
return view == obj
......@@ -20,29 +20,25 @@ class CategoryPagerAdapter(val listener: EmojiKeyboardListener) : PagerAdapter()
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val view = LayoutInflater.from(container.context)
.inflate(R.layout.emoji_category_layout, container, false)
val layoutManager = GridLayoutManager(view.context, 8)
val recycler = view.findViewById(R.id.emojiRecyclerView) as RecyclerView
val adapter = EmojiAdapter(layoutManager.spanCount, listener)
val category = EmojiCategory.values().get(position)
val emojiNoRecentText : TextView = view.findViewById(R.id.text_no_recent_emoji)
val emojis = if (category != EmojiCategory.RECENTS) {
EmojiRepository.getEmojisByCategory(category)
} else {
EmojiRepository.getRecents()
}
val recentEmojiSize = EmojiRepository.getRecents().size
if (category == EmojiCategory.RECENTS && recentEmojiSize == 0){
emojiNoRecentText.setVisible(true)
}else{
emojiNoRecentText.setVisible(false)
.inflate(R.layout.emoji_category_layout, container, false)
with(view) {
val layoutManager = GridLayoutManager(context, 8)
val adapter = EmojiAdapter(layoutManager.spanCount, listener)
val category = EmojiCategory.values()[position]
val emojis = if (category != EmojiCategory.RECENTS) {
EmojiRepository.getEmojisByCategory(category)
} else {
EmojiRepository.getRecents()
}
val recentEmojiSize = EmojiRepository.getRecents().size
text_no_recent_emoji.isVisible = category == EmojiCategory.RECENTS && recentEmojiSize == 0
adapter.addEmojis(emojis)
emoji_recycler_view.layoutManager = layoutManager
emoji_recycler_view.itemAnimator = DefaultItemAnimator()
emoji_recycler_view.adapter = adapter
emoji_recycler_view.isNestedScrollingEnabled = false
container.addView(view)
}
adapter.addEmojis(emojis)
recycler.layoutManager = layoutManager
recycler.itemAnimator = DefaultItemAnimator()
recycler.adapter = adapter
recycler.isNestedScrollingEnabled = false
container.addView(view)
return view
}
......@@ -54,7 +50,11 @@ class CategoryPagerAdapter(val listener: EmojiKeyboardListener) : PagerAdapter()
override fun getPageTitle(position: Int) = EmojiCategory.values()[position].textIcon()
class EmojiAdapter(val spanCount: Int, val listener: EmojiKeyboardListener) : RecyclerView.Adapter<EmojiRowViewHolder>() {
class EmojiAdapter(
private val spanCount: Int,
private val listener: EmojiKeyboardListener
) : RecyclerView.Adapter<EmojiRowViewHolder>() {
private var emojis = Collections.emptyList<Emoji>()
fun addEmojis(emojis: List<Emoji>) {
......@@ -74,7 +74,13 @@ class CategoryPagerAdapter(val listener: EmojiKeyboardListener) : PagerAdapter()
override fun getItemCount(): Int = emojis.size
}
class EmojiRowViewHolder(itemView: View, val itemCount: Int, val spanCount: Int, val listener: EmojiKeyboardListener) : RecyclerView.ViewHolder(itemView) {
class EmojiRowViewHolder(
itemView: View,
private val itemCount: Int,
private val spanCount: Int,
private val listener: EmojiKeyboardListener
) : RecyclerView.ViewHolder(itemView) {
private val emojiView: TextView = itemView.findViewById(R.id.emoji)
fun bind(emoji: Emoji) {
......
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.content.Context
import androidx.appcompat.widget.AppCompatEditText
......
package chat.rocket.android.emoji
data class Emoji(
val shortname: String,
val shortnameAlternates: List<String>,
val unicode: String,
val keywords: List<String>,
val category: String,
val count: Int = 0
)
\ No newline at end of file
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import androidx.annotation.DrawableRes
import android.text.SpannableString
import android.text.Spanned
import chat.rocket.android.R
import androidx.annotation.DrawableRes
enum class EmojiCategory {
RECENTS {
......
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
interface EmojiKeyboardListener {
/**
* When an emoji is selected on the picker.
* Callback when an emoji is selected on the picker (optional operation).
*
* @param emoji The selected emoji
*/
fun onEmojiAdded(emoji: Emoji)
fun onEmojiAdded(emoji: Emoji) {}
/**
* When backspace key is clicked.
* Callback when backspace key is clicked (optional operation).
*
* @param keyCode The key code pressed as defined
*
* @see android.view.KeyEvent
*/
fun onNonEmojiKeyPressed(keyCode: Int)
fun onNonEmojiKeyPressed(keyCode: Int) {}
}
\ No newline at end of file
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.content.Context
import com.google.android.material.tabs.TabLayout
import androidx.viewpager.widget.ViewPager
import androidx.appcompat.app.AppCompatActivity
import android.text.Editable
import android.text.TextWatcher
import android.view.KeyEvent
......@@ -12,10 +9,16 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import chat.rocket.android.R
import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow(context, view) {
class EmojiKeyboardPopup(
context: Context,
view: View
) : OverKeyboardPopupWindow(context, view) {
private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout
private lateinit var searchView: View
......@@ -23,10 +26,6 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
private lateinit var parentContainer: ViewGroup
var listener: EmojiKeyboardListener? = null
companion object {
const val PREF_EMOJI_RECENTS = "PREF_EMOJI_RECENTS"
}
override fun onCreateView(inflater: LayoutInflater): View {
val view = inflater.inflate(R.layout.emoji_keyboard, null)
parentContainer = view.findViewById(R.id.emoji_keyboard_container)
......@@ -58,14 +57,14 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
is EmojiKeyboardListener -> it
else -> {
val fragments = (it as AppCompatActivity).supportFragmentManager.fragments
if (fragments == null || fragments.size == 0 || !(fragments[0] is EmojiKeyboardListener)) {
if (fragments.size == 0 || !(fragments[0] is EmojiKeyboardListener)) {
throw IllegalStateException("activity/fragment should implement Listener interface")
}
fragments[0] as EmojiKeyboardListener
}
}
viewPager.adapter = CategoryPagerAdapter(object : EmojiListenerAdapter() {
viewPager.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
callback.onEmojiAdded(emoji)
......@@ -86,11 +85,11 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
}
}
class EmojiTextWatcher(val editor: EditText) : TextWatcher {
class EmojiTextWatcher(private val editor: EditText) : TextWatcher {
@Volatile private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
override fun afterTextChanged(s: Editable) {
val message = editor.getEditableText()
val message = editor.editableText
// Commit the emoticons to be removed.
for (span in emojiToRemove.toList()) {
......@@ -112,7 +111,7 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
if (after < count) {
val end = start + count
val message = editor.getEditableText()
val message = editor.editableText
val list = message.getSpans(start, end, EmojiTypefaceSpan::class.java)
for (span in list) {
......@@ -129,4 +128,8 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
}
companion object {
const val PREF_EMOJI_RECENTS = "PREF_EMOJI_RECENTS"
}
}
\ No newline at end of file
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.text.SpannableString
import android.text.Spanned
class EmojiParser {
companion object {
/**
* Parses a text string containing unicode characters and/or shortnames to a rendered
......@@ -14,7 +15,7 @@ class EmojiParser {
*/
fun parse(text: CharSequence): CharSequence {
val unicodedText = EmojiRepository.shortnameToUnicode(text, true)
var spannable = SpannableString.valueOf(unicodedText)
val spannable = SpannableString.valueOf(unicodedText)
val typeface = EmojiRepository.cachedTypeface
// Look for groups of emojis, set a EmojiTypefaceSpan with the emojione font.
val length = spannable.length
......@@ -49,12 +50,5 @@ class EmojiParser {
}
return spannable
}
private fun calculateSurrogatePairs(scalar: Int): Pair<Int, Int> {
val temp: Int = (scalar - 0x10000) / 0x400
val s1: Int = Math.floor(temp.toDouble()).toInt() + 0xD800
val s2: Int = ((scalar - 0x10000) % 0x400) + 0xDC00
return Pair(s1, s2)
}
}
}
\ No newline at end of file
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.app.Dialog
import android.content.Context
......@@ -9,12 +9,11 @@ import android.view.LayoutInflater
import android.view.Window
import android.view.WindowManager
import android.widget.ImageView
import chat.rocket.android.R
import kotlinx.android.synthetic.main.emoji_picker.*
class EmojiPickerPopup(context: Context) : Dialog(context) {
private lateinit var viewPager: ViewPager
private lateinit var tabLayout: TabLayout
var listener: EmojiKeyboardListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
......@@ -22,9 +21,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.emoji_picker)
viewPager = findViewById(R.id.pager_categories)
tabLayout = findViewById(R.id.tabs)
tabLayout.setupWithViewPager(viewPager)
tabs.setupWithViewPager(pager_categories)
setupViewPager()
setSize()
}
......@@ -38,7 +35,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
}
private fun setupViewPager() {
viewPager.adapter = CategoryPagerAdapter(object : EmojiListenerAdapter() {
pager_categories.adapter = CategoryPagerAdapter(object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) {
EmojiRepository.addToRecents(emoji)
dismiss()
......@@ -47,7 +44,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
})
for (category in EmojiCategory.values()) {
val tab = tabLayout.getTabAt(category.ordinal)
val tab = tabs.getTabAt(category.ordinal)
val tabView = LayoutInflater.from(context).inflate(R.layout.emoji_picker_tab, null)
tab?.customView = tabView
val textView = tabView.findViewById(R.id.image_category) as ImageView
......@@ -56,6 +53,6 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
val currentTab = if (EmojiRepository.getRecents().isEmpty()) EmojiCategory.PEOPLE.ordinal else
EmojiCategory.RECENTS.ordinal
viewPager.currentItem = currentTab
pager_categories.currentItem = currentTab
}
}
\ No newline at end of file
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
interface EmojiReactionListener {
/**
......
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Typeface
import android.os.Build
import org.json.JSONArray
import org.json.JSONObject
import java.io.BufferedReader
......@@ -54,10 +53,6 @@ object EmojiRepository {
*/
fun getAll() = ALL_EMOJIS
// fun findEmojiByUnicode(unicode: Int) {
// ALL_EMOJIS.find { it.unicode == }
// }
/**
* Get all emojis for a given category.
*
......@@ -108,7 +103,7 @@ object EmojiRepository {
list.add(it.copy(count = useCount))
}
}
Collections.sort(list, { o1, o2 ->
list.sortWith(Comparator { o1, o2 ->
o2.count - o1.count
})
return list
......@@ -147,10 +142,10 @@ object EmojiRepository {
return null
}
return Emoji(shortname = json.getString("shortname"),
unicode = json.getString("unicode"),
shortnameAlternates = buildStringListFromJsonArray(json.getJSONArray("shortnameAlternates")),
category = json.getString("category"),
keywords = buildStringListFromJsonArray(json.getJSONArray("keywords")))
unicode = json.getString("unicode"),
shortnameAlternates = buildStringListFromJsonArray(json.getJSONArray("shortnameAlternates")),
category = json.getString("category"),
keywords = buildStringListFromJsonArray(json.getJSONArray("keywords")))
}
private fun buildStringListFromJsonArray(array: JSONArray): List<String> {
......
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.graphics.Paint
import android.graphics.Typeface
......
......@@ -16,21 +16,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package chat.rocket.android.widget.emoji
package chat.rocket.android.emoji
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.Build
import android.view.*
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowManager
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 {
abstract class OverKeyboardPopupWindow(
val context: Context,
private val rootView: View
) : PopupWindow(context), ViewTreeObserver.OnGlobalLayoutListener {
/**
* @return keyboard height in pixels
......@@ -154,6 +159,4 @@ abstract class OverKeyboardPopupWindow(val context: Context, private val rootVie
abstract fun onCreateView(inflater: LayoutInflater): View
abstract fun onViewCreated(view: View)
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/emojiRecyclerView"
android:id="@+id/emoji_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_marginStart="8dp" />
<TextView
android:id="@+id/text_no_recent_emoji"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorWhite">#FFFFFFFF</color>
<color name="darkGray">#FFa0a0a0</color>
<color name="whitesmoke">#FFf1f1f1</color>
<color name="colorEmojiIcon">#FF767676</color>
<color name="colorDividerMessageComposer">#D8D8D8</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen>
<dimen name="picker_popup_height">250dp</dimen>
<dimen name="picker_popup_width">300dp</dimen>
</resources>
\ No newline at end of file
<resources>
<string name="msg_no_recent_emoji">No recent emoji</string>
</resources>
......@@ -7,7 +7,7 @@ android {
buildToolsVersion versions.buildTools
defaultConfig {
minSdkVersion 21
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 1
versionName "1.0.0"
......
include ':app', ':player' //, ':wear'
\ No newline at end of file
include ':app', ':player', ':emoji' //, ':wear'
\ 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