Unverified Commit 3383b0f2 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge pull request #1238 from RocketChat/new/send-typing-status

[NEW] Send typing status feature.
parents 9f55d568 90d88377
...@@ -13,8 +13,8 @@ android { ...@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2019 versionCode 2020
versionName "2.1.0" versionName "2.2.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
......
...@@ -14,6 +14,7 @@ import chat.rocket.android.core.behaviours.showMessage ...@@ -14,6 +14,7 @@ import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.username
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state import chat.rocket.android.server.infraestructure.state
...@@ -26,6 +27,7 @@ import chat.rocket.common.model.SimpleUser ...@@ -26,6 +27,7 @@ import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.realtime.setTypingStatus
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.rest.* import chat.rocket.core.internal.rest.*
import chat.rocket.core.model.Command import chat.rocket.core.model.Command
...@@ -45,8 +47,6 @@ class ChatRoomPresenter @Inject constructor( ...@@ -45,8 +47,6 @@ class ChatRoomPresenter @Inject constructor(
private val view: ChatRoomView, private val view: ChatRoomView,
private val navigator: ChatRoomNavigator, private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val permissions: PermissionsInteractor, private val permissions: PermissionsInteractor,
private val uriInteractor: UriInteractor, private val uriInteractor: UriInteractor,
...@@ -55,15 +55,17 @@ class ChatRoomPresenter @Inject constructor( ...@@ -55,15 +55,17 @@ class ChatRoomPresenter @Inject constructor(
private val roomsRepository: RoomRepository, private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val userHelper: UserHelper, private val userHelper: UserHelper,
factory: ConnectionManagerFactory,
private val mapper: ViewModelMapper, private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor private val jobSchedulerInteractor: JobSchedulerInteractor,
getSettingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer) private val manager = factory.create(currentServer)
private val client = manager.client private val client = manager.client
private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!) private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!)
private val currentLoggedUsername = localRepository.username()
private val messagesChannel = Channel<Message>() private val messagesChannel = Channel<Message>()
private var chatRoomId: String? = null private var chatRoomId: String? = null
...@@ -212,6 +214,22 @@ class ChatRoomPresenter @Inject constructor( ...@@ -212,6 +214,22 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
fun sendTyping() {
launch(CommonPool + strategy.jobs) {
if (chatRoomId != null && currentLoggedUsername != null) {
client.setTypingStatus(chatRoomId.toString(), currentLoggedUsername, true)
}
}
}
fun sendNotTyping() {
launch(CommonPool + strategy.jobs) {
if (chatRoomId != null && currentLoggedUsername != null) {
client.setTypingStatus(chatRoomId.toString(), currentLoggedUsername, false)
}
}
}
private fun markRoomAsRead(roomId: String) { private fun markRoomAsRead(roomId: String) {
launchUI(strategy) { launchUI(strategy) {
try { try {
......
...@@ -30,11 +30,14 @@ import chat.rocket.android.util.extensions.* ...@@ -30,11 +30,14 @@ import chat.rocket.android.util.extensions.*
import chat.rocket.android.widget.emoji.* import chat.rocket.android.widget.emoji.*
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.* import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.* import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
...@@ -154,7 +157,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -154,7 +157,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.unsubscribeMessages(chatRoomId) presenter.unsubscribeMessages(chatRoomId)
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
unsubscribeTextMessage() unsubscribeComposeTextMessage()
// Hides the keyboard (if it's opened) before going to any view. // Hides the keyboard (if it's opened) before going to any view.
activity?.apply { activity?.apply {
...@@ -577,7 +580,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -577,7 +580,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_show_attachment_options.alpha = 1f button_show_attachment_options.alpha = 1f
button_show_attachment_options.setVisible(true) button_show_attachment_options.setVisible(true)
subscribeTextMessage() subscribeComposeTextMessage()
emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this emojiKeyboardPopup.listener = this
text_message.listener = object : ComposerEditText.ComposerEditTextListener { text_message.listener = object : ComposerEditText.ComposerEditTextListener {
...@@ -682,18 +685,30 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -682,18 +685,30 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}) })
} }
private fun subscribeTextMessage() { private fun subscribeComposeTextMessage() {
val disposable = text_message.asObservable(0) val editTextObservable = text_message.asObservable()
.subscribe({ t -> setupComposeMessageButtons(t) })
compositeDisposable.add(disposable) compositeDisposable.addAll(
subscribeComposeButtons(editTextObservable),
subscribeComposeTypingStatus(editTextObservable)
)
} }
private fun unsubscribeTextMessage() { private fun unsubscribeComposeTextMessage() {
compositeDisposable.clear() compositeDisposable.clear()
} }
private fun setupComposeMessageButtons(charSequence: CharSequence) { private fun subscribeComposeButtons(observable: Observable<CharSequence>): Disposable {
return observable.subscribe { t -> setupComposeButtons(t) }
}
private fun subscribeComposeTypingStatus(observable: Observable<CharSequence>): Disposable {
return observable.debounce(300, TimeUnit.MILLISECONDS)
.skip(1)
.subscribe { t -> sendTypingStatus(t) }
}
private fun setupComposeButtons(charSequence: CharSequence) {
if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) { if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) {
button_show_attachment_options.fadeOut(1F, 0F, 120) button_show_attachment_options.fadeOut(1F, 0F, 120)
button_send.fadeIn(0F, 1F, 120) button_send.fadeIn(0F, 1F, 120)
...@@ -707,6 +722,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -707,6 +722,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
private fun sendTypingStatus(charSequence: CharSequence) {
if (charSequence.isNotBlank()) {
presenter.sendTyping()
} else {
presenter.sendNotTyping()
}
}
private fun showAttachmentOptions() { private fun showAttachmentOptions() {
view_dim.setVisible(true) view_dim.setVisible(true)
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.main.viewmodel ...@@ -2,7 +2,6 @@ package chat.rocket.android.main.viewmodel
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
data class NavHeaderViewModel( data class NavHeaderViewModel(
val userDisplayName: String?, val userDisplayName: String?,
val userStatus: UserStatus?, val userStatus: UserStatus?,
......
...@@ -6,9 +6,8 @@ import io.reactivex.Observable ...@@ -6,9 +6,8 @@ import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
fun EditText.asObservable(debounceTimeout: Long = 100): Observable<CharSequence> { fun EditText.asObservable(): Observable<CharSequence> {
return RxTextView.textChanges(this) return RxTextView.textChanges(this)
.debounce(debounceTimeout, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.mainThread()) .subscribeOn(AndroidSchedulers.mainThread())
} }
\ 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