Commit 15abcd8c authored by Leonardo Aramaki's avatar Leonardo Aramaki

Merge branch 'develop-2.x' into new/offline-sending

parents cea99c1d eb838602
......@@ -86,31 +86,39 @@ class LoginFragment : Fragment(), LoginView {
}
private fun tintEditTextDrawableStart() {
activity?.apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
ui {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it)
val drawables = arrayOf(personDrawable, lockDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_username_or_email, text_password), drawables)
}
}
override fun showLoading() {
view_loading.setVisible(true)
ui {
view_loading.setVisible(true)
}
}
override fun hideLoading() {
view_loading.setVisible(false)
ui {
view_loading.setVisible(false)
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() {
......@@ -118,152 +126,208 @@ class LoginFragment : Fragment(), LoginView {
}
override fun showFormView() {
text_username_or_email.setVisible(true)
text_password.setVisible(true)
ui {
text_username_or_email.setVisible(true)
text_password.setVisible(true)
}
}
override fun hideFormView() {
text_username_or_email.setVisible(false)
text_password.setVisible(false)
ui {
text_username_or_email.setVisible(false)
text_password.setVisible(false)
}
}
override fun setupLoginButtonListener() {
button_log_in.setOnClickListener {
presenter.authenticateWithUserAndPassword(text_username_or_email.textContent, text_password.textContent)
ui {
button_log_in.setOnClickListener {
presenter.authenticateWithUserAndPassword(text_username_or_email.textContent,
text_password.textContent)
}
}
}
override fun enableUserInput() {
button_log_in.isEnabled = true
text_username_or_email.isEnabled = true
text_password.isEnabled = true
ui {
button_log_in.isEnabled = true
text_username_or_email.isEnabled = true
text_password.isEnabled = true
}
}
override fun disableUserInput() {
button_log_in.isEnabled = false
text_username_or_email.isEnabled = false
text_password.isEnabled = false
ui {
button_log_in.isEnabled = false
text_username_or_email.isEnabled = false
text_password.isEnabled = false
}
}
override fun showCasButton() {
button_cas.setVisible(true)
ui {
button_cas.setVisible(true)
}
}
override fun hideCasButton() {
button_cas.setVisible(false)
ui {
button_cas.setVisible(false)
}
}
override fun setupCasButtonListener(casUrl: String, casToken: String) {
button_cas.setOnClickListener {
startActivityForResult(context?.casWebViewIntent(casUrl, casToken), REQUEST_CODE_FOR_CAS)
activity?.overridePendingTransition(R.anim.slide_up, R.anim.hold)
ui { activity ->
button_cas.setOnClickListener {
startActivityForResult(activity.casWebViewIntent(casUrl, casToken),
REQUEST_CODE_FOR_CAS)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun showSignUpView() {
text_new_to_rocket_chat.setVisible(true)
ui {
text_new_to_rocket_chat.setVisible(true)
}
}
override fun setupSignUpView() {
val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
ui {
val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
text_new_to_rocket_chat.text = newToRocketChat
text_new_to_rocket_chat.text = newToRocketChat
val signUpListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.signup()
}
val signUpListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.signup()
}
TextHelper.addLink(text_new_to_rocket_chat, arrayOf(signUp), arrayOf(signUpListener))
TextHelper.addLink(text_new_to_rocket_chat, arrayOf(signUp), arrayOf(signUpListener))
}
}
override fun hideSignUpView() {
text_new_to_rocket_chat.setVisible(false)
ui {
text_new_to_rocket_chat.setVisible(false)
}
}
override fun enableOauthView() {
isOauthViewEnable = true
showThreeSocialAccountsMethods()
social_accounts_container.setVisible(true)
ui {
isOauthViewEnable = true
showThreeSocialAccountsMethods()
social_accounts_container.setVisible(true)
}
}
override fun disableOauthView() {
isOauthViewEnable = false
social_accounts_container.setVisible(false)
ui {
isOauthViewEnable = false
social_accounts_container.setVisible(false)
}
}
override fun showLoginButton() {
button_log_in.setVisible(true)
ui {
button_log_in.setVisible(true)
}
}
override fun hideLoginButton() {
button_log_in.setVisible(false)
ui {
button_log_in.setVisible(false)
}
}
override fun enableLoginByFacebook() {
button_facebook.isClickable = true
ui {
button_facebook.isClickable = true
}
}
override fun enableLoginByGithub() {
button_github.isClickable = true
ui {
button_github.isClickable = true
}
}
override fun setupGithubButtonListener(githubUrl: String, state: String) {
button_github.setOnClickListener {
startActivityForResult(context?.oauthWebViewIntent(githubUrl, state), REQUEST_CODE_FOR_OAUTH)
activity?.overridePendingTransition(R.anim.slide_up, R.anim.hold)
ui { activity ->
button_github.setOnClickListener {
startActivityForResult(activity.oauthWebViewIntent(githubUrl, state), REQUEST_CODE_FOR_OAUTH)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGoogle() {
button_google.isClickable = true
ui {
button_google.isClickable = true
}
}
// TODO: Use custom tabs instead of web view. See https://github.com/RocketChat/Rocket.Chat.Android/issues/968
override fun setupGoogleButtonListener(googleUrl: String, state: String) {
button_google.setOnClickListener {
startActivityForResult(context?.oauthWebViewIntent(googleUrl, state), REQUEST_CODE_FOR_OAUTH)
activity?.overridePendingTransition(R.anim.slide_up, R.anim.hold)
ui { activity ->
button_google.setOnClickListener {
startActivityForResult(activity.oauthWebViewIntent(googleUrl, state), REQUEST_CODE_FOR_OAUTH)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByLinkedin() {
button_linkedin.isClickable = true
ui {
button_linkedin.isClickable = true
}
}
override fun setupLinkedinButtonListener(linkedinUrl: String, state: String) {
button_linkedin.setOnClickListener {
startActivityForResult(context?.oauthWebViewIntent(linkedinUrl, state), REQUEST_CODE_FOR_OAUTH)
activity?.overridePendingTransition(R.anim.slide_up, R.anim.hold)
ui { activity ->
button_linkedin.setOnClickListener {
startActivityForResult(activity.oauthWebViewIntent(linkedinUrl, state), REQUEST_CODE_FOR_OAUTH)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByMeteor() {
button_meteor.isClickable = true
ui {
button_meteor.isClickable = true
}
}
override fun enableLoginByTwitter() {
button_twitter.isClickable = true
ui {
button_twitter.isClickable = true
}
}
override fun enableLoginByGitlab() {
button_gitlab.isClickable = true
ui {
button_gitlab.isClickable = true
}
}
override fun setupGitlabButtonListener(gitlabUrl: String, state: String) {
button_gitlab.setOnClickListener {
startActivityForResult(context?.oauthWebViewIntent(gitlabUrl, state), REQUEST_CODE_FOR_OAUTH)
activity?.overridePendingTransition(R.anim.slide_up, R.anim.hold)
ui { activity ->
button_gitlab.setOnClickListener {
startActivityForResult(activity.oauthWebViewIntent(gitlabUrl, state), REQUEST_CODE_FOR_OAUTH)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun setupFabListener() {
button_fab.setVisible(true)
button_fab.setOnClickListener({
button_fab.hide()
showRemainingSocialAccountsView()
scrollToBottom()
})
ui {
button_fab.setVisible(true)
button_fab.setOnClickListener({
button_fab.hide()
showRemainingSocialAccountsView()
scrollToBottom()
})
}
}
override fun setupGlobalListener() {
......@@ -276,19 +340,23 @@ class LoginFragment : Fragment(), LoginView {
}
override fun alertWrongUsernameOrEmail() {
vibrateSmartPhone()
text_username_or_email.shake()
text_username_or_email.requestFocus()
ui {
vibrateSmartPhone()
text_username_or_email.shake()
text_username_or_email.requestFocus()
}
}
override fun alertWrongPassword() {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
ui {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
}
}
override fun alertNotRecommendedVersion() {
context?.let {
ui {
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, null)
......@@ -298,7 +366,7 @@ class LoginFragment : Fragment(), LoginView {
}
override fun blockAndAlertNotRequiredVersion() {
context?.let {
ui {
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
.setOnDismissListener { activity?.onBackPressed() }
......@@ -313,14 +381,14 @@ class LoginFragment : Fragment(), LoginView {
(0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.forEach { it.setVisible(true)}
.forEach { ui { it.setVisible(true) }}
}, 1000)
}
// Scrolling to the bottom of the screen.
private fun scrollToBottom() {
scroll_view.postDelayed({
scroll_view.fullScroll(ScrollView.FOCUS_DOWN)
ui { scroll_view.fullScroll(ScrollView.FOCUS_DOWN) }
}, 1250)
}
......@@ -350,13 +418,13 @@ class LoginFragment : Fragment(), LoginView {
.forEach { it.setVisible(true) }
}
fun showOauthView() {
private fun showOauthView() {
if (isOauthViewEnable) {
social_accounts_container.setVisible(true)
}
}
fun hideOauthView() {
private fun hideOauthView() {
if (isOauthViewEnable) {
social_accounts_container.setVisible(false)
button_fab.setVisible(false)
......
......@@ -59,26 +59,36 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
}
override fun alertBlankUsername() {
vibrateSmartPhone()
text_username.shake()
ui {
vibrateSmartPhone()
text_username.shake()
}
}
override fun showLoading() {
disableUserInput()
view_loading.setVisible(true)
ui {
disableUserInput()
view_loading.setVisible(true)
}
}
override fun hideLoading() {
view_loading.setVisible(false)
enableUserInput()
ui {
view_loading.setVisible(false)
enableUserInput()
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() {
......@@ -86,10 +96,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
}
private fun tintEditTextDrawableStart() {
activity?.apply {
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
ui {
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, it)
DrawableHelper.wrapDrawable(atDrawable)
DrawableHelper.tintDrawable(atDrawable, this, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawable(atDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_username, atDrawable)
}
}
......
......@@ -47,21 +47,29 @@ class ServerFragment : Fragment(), ServerView {
override fun showInvalidServerUrlMessage() = showMessage(getString(R.string.msg_invalid_server_url))
override fun showLoading() {
enableUserInput(false)
view_loading.setVisible(true)
ui {
enableUserInput(false)
view_loading.setVisible(true)
}
}
override fun hideLoading() {
view_loading.setVisible(false)
enableUserInput(true)
ui {
view_loading.setVisible(false)
enableUserInput(true)
}
}
override fun showMessage(resId: Int){
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() {
......
......@@ -25,7 +25,7 @@ class SignupFragment : Fragment(), SignupView {
} else {
bottom_container.apply {
postDelayed({
setVisible(true)
ui { setVisible(true) }
}, 3)
}
}
......@@ -64,45 +64,61 @@ class SignupFragment : Fragment(), SignupView {
}
override fun alertBlankName() {
vibrateSmartPhone()
text_name.shake()
text_name.requestFocus()
ui {
vibrateSmartPhone()
text_name.shake()
text_name.requestFocus()
}
}
override fun alertBlankUsername() {
vibrateSmartPhone()
text_username.shake()
text_username.requestFocus()
ui {
vibrateSmartPhone()
text_username.shake()
text_username.requestFocus()
}
}
override fun alertEmptyPassword() {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
ui {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
}
}
override fun alertBlankEmail() {
vibrateSmartPhone()
text_email.shake()
text_email.requestFocus()
ui {
vibrateSmartPhone()
text_email.shake()
text_email.requestFocus()
}
}
override fun showLoading() {
enableUserInput(false)
view_loading.setVisible(true)
ui {
enableUserInput(false)
view_loading.setVisible(true)
}
}
override fun hideLoading() {
view_loading.setVisible(false)
enableUserInput(true)
ui {
view_loading.setVisible(false)
enableUserInput(true)
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() {
......@@ -110,15 +126,15 @@ class SignupFragment : Fragment(), SignupView {
}
private fun tintEditTextDrawableStart() {
activity?.apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
ui {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, it)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, it)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, it)
val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables)
}
}
......
......@@ -63,8 +63,10 @@ class TwoFAFragment : Fragment(), TwoFAView {
}
override fun alertBlankTwoFactorAuthenticationCode() {
vibrateSmartPhone()
text_two_factor_auth.shake()
ui {
vibrateSmartPhone()
text_two_factor_auth.shake()
}
}
override fun alertInvalidTwoFactorAuthenticationCode() {
......@@ -72,30 +74,38 @@ class TwoFAFragment : Fragment(), TwoFAView {
}
override fun showLoading() {
enableUserInput(false)
view_loading.setVisible(true)
ui {
enableUserInput(false)
view_loading.setVisible(true)
}
}
override fun hideLoading() {
view_loading.setVisible(false)
enableUserInput(true)
ui {
view_loading.setVisible(false)
enableUserInput(true)
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun tintEditTextDrawableStart() {
activity?.apply {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, this)
ui {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, it)
DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, this, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawable(lockDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
}
......
......@@ -4,7 +4,6 @@ import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
......@@ -18,6 +17,7 @@ import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.*
import androidx.core.content.systemService
import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.*
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
......@@ -41,7 +41,6 @@ import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject
import kotlin.math.absoluteValue
fun newInstance(chatRoomId: String,
chatRoomName: String,
......@@ -185,31 +184,31 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun showMessages(dataSet: List<BaseViewModel<*>>) {
// track the message sent immediately after the current message
var prevMessageViewModel: MessageViewModel? = null
// Loop over received messages to determine first unread
for (i in dataSet.indices) {
val msgModel = dataSet[i]
if (msgModel is MessageViewModel) {
val msg = msgModel.rawData
if (msg.timestamp < chatRoomLastSeen) {
// This message was sent before the last seen of the room. Hence, it was seen.
// if there is a message after (below) this, mark it firstUnread.
if (prevMessageViewModel != null) {
prevMessageViewModel.isFirstUnread = true
ui {
// track the message sent immediately after the current message
var prevMessageViewModel: MessageViewModel? = null
// Loop over received messages to determine first unread
for (i in dataSet.indices) {
val msgModel = dataSet[i]
if (msgModel is MessageViewModel) {
val msg = msgModel.rawData
if (msg.timestamp < chatRoomLastSeen) {
// This message was sent before the last seen of the room. Hence, it was seen.
// if there is a message after (below) this, mark it firstUnread.
if (prevMessageViewModel != null) {
prevMessageViewModel.isFirstUnread = true
}
break
}
break
prevMessageViewModel = msgModel
}
prevMessageViewModel = msgModel
}
}
activity?.apply {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, presenter,
reactionListener = this@ChatRoomFragment)
reactionListener = this@ChatRoomFragment)
recycler_view.adapter = adapter
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener)
......@@ -284,11 +283,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun sendMessage(text: String) {
if (!text.isBlank()) {
if (!text.startsWith("/")) {
presenter.sendMessage(chatRoomId, text, editingMessageId)
} else {
presenter.runCommand(text, chatRoomId)
ui {
if (!text.isBlank()) {
if (!text.startsWith("/")) {
presenter.sendMessage(chatRoomId, text, editingMessageId)
} else {
presenter.runCommand(text, chatRoomId)
}
}
}
}
......@@ -303,41 +304,54 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun showNewMessage(message: List<BaseViewModel<*>>) {
adapter.prependData(message)
recycler_view.scrollToPosition(0)
verticalScrollOffset.set(0)
ui {
adapter.prependData(message)
recycler_view.scrollToPosition(0)
verticalScrollOffset.set(0)
}
}
override fun disableSendMessageButton() {
button_send.isEnabled = false
ui {
button_send.isEnabled = false
}
}
override fun enableSendMessageButton() {
button_send.isEnabled = true
text_message.isEnabled = true
clearMessageComposition()
ui {
button_send.isEnabled = true
text_message.isEnabled = true
clearMessageComposition()
}
}
override fun clearMessageComposition() {
citation = null
editingMessageId = null
text_message.textContent = ""
actionSnackbar.dismiss()
ui {
citation = null
editingMessageId = null
text_message.textContent = ""
actionSnackbar.dismiss()
}
}
override fun dispatchUpdateMessage(index: Int, message: List<BaseViewModel<*>>) {
adapter.updateItem(message.last())
if (message.size > 1) {
adapter.prependData(listOf(message.first()))
ui {
adapter.updateItem(message.last())
if (message.size > 1) {
adapter.prependData(listOf(message.first()))
}
}
}
override fun dispatchDeleteMessage(msgId: String) {
adapter.removeItem(msgId)
ui {
adapter.removeItem(msgId)
}
}
override fun showReplyingAction(username: String, replyMarkdown: String, quotedMessage: String) {
activity?.apply {
ui {
citation = replyMarkdown
actionSnackbar.title = username
actionSnackbar.text = quotedMessage
......@@ -352,41 +366,55 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override fun showLoading() = view_loading.setVisible(true)
override fun showLoading() {
ui { view_loading.setVisible(true) }
}
override fun hideLoading() = view_loading.setVisible(false)
override fun hideLoading() {
ui { view_loading.setVisible(false) }
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun populatePeopleSuggestions(members: List<PeopleSuggestionViewModel>) {
suggestions_view.addItems("@", members)
ui {
suggestions_view.addItems("@", members)
}
}
override fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionViewModel>) {
suggestions_view.addItems("#", chatRooms)
ui {
suggestions_view.addItems("#", chatRooms)
}
}
override fun populateCommandSuggestions(commands: List<CommandSuggestionViewModel>) {
suggestions_view.addItems("/", commands)
ui {
suggestions_view.addItems("/", commands)
}
}
override fun copyToClipboard(message: String) {
activity?.apply {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
ui {
val clipboard: ClipboardManager = it.systemService()
clipboard.primaryClip = ClipData.newPlainText("", message)
}
}
override fun showEditingAction(roomId: String, messageId: String, text: String) {
activity?.apply {
ui {
actionSnackbar.title = getString(R.string.action_title_editing)
actionSnackbar.text = text
actionSnackbar.show()
......@@ -422,7 +450,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun showReactionsPopup(messageId: String) {
context?.let {
ui {
val emojiPickerPopup = EmojiPickerPopup(it)
emojiPickerPopup.listener = object : EmojiListenerAdapter() {
override fun onEmojiAdded(emoji: Emoji) {
......@@ -435,16 +463,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setReactionButtonIcon(@DrawableRes drawableId: Int) {
button_add_reaction.setImageResource(drawableId)
button_add_reaction.setTag(drawableId)
button_add_reaction.tag = drawableId
}
override fun showFileSelection(filter: Array<String>) {
activity?.let {
ui {
if (ContextCompat.checkSelfPermission(it, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(it,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
1)
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
1)
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
......@@ -459,7 +487,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
1 -> {
if (!(grantResults.isNotEmpty() && grantResults.first() == PackageManager.PERMISSION_GRANTED)) {
handler.postDelayed({
hideAttachmentOptions()
ui { hideAttachmentOptions() }
}, 400)
}
}
......@@ -471,7 +499,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun showConnectionState(state: State) {
activity?.apply {
ui {
connection_status_text.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
......@@ -489,10 +517,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override fun onJoined() {
input_container.setVisible(true)
button_join_chat.setVisible(false)
isSubscribed = true
setupMessageComposer()
ui {
input_container.setVisible(true)
button_join_chat.setVisible(false)
isSubscribed = true
setupMessageComposer()
}
}
private val dismissStatus = {
......@@ -506,7 +536,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator()
endlessRecyclerViewScrollListener = object :
EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) {
EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
presenter.loadMessages(chatRoomId, chatRoomType, page * 30L)
}
......@@ -592,23 +622,23 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupSuggestionsView() {
suggestions_view.anchorTo(text_message)
.setMaximumHeight(resources.getDimensionPixelSize(R.dimen.suggestions_box_max_height))
.addTokenAdapter(PeopleSuggestionsAdapter(context!!))
.addTokenAdapter(CommandSuggestionsAdapter())
.addTokenAdapter(RoomSuggestionsAdapter())
.addSuggestionProviderAction("@") { query ->
if (query.isNotEmpty()) {
presenter.spotlight(query, PEOPLE, true)
}
}
.addSuggestionProviderAction("#") { query ->
if (query.isNotEmpty()) {
presenter.loadChatRooms()
}
.setMaximumHeight(resources.getDimensionPixelSize(R.dimen.suggestions_box_max_height))
.addTokenAdapter(PeopleSuggestionsAdapter(context!!))
.addTokenAdapter(CommandSuggestionsAdapter())
.addTokenAdapter(RoomSuggestionsAdapter())
.addSuggestionProviderAction("@") { query ->
if (query.isNotEmpty()) {
presenter.spotlight(query, PEOPLE, true)
}
.addSuggestionProviderAction("/") { _ ->
presenter.loadCommands()
}
.addSuggestionProviderAction("#") { query ->
if (query.isNotEmpty()) {
presenter.loadChatRooms()
}
}
.addSuggestionProviderAction("/") { _ ->
presenter.loadCommands()
}
presenter.loadCommands()
}
......@@ -643,7 +673,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun subscribeTextMessage() {
val disposable = text_message.asObservable(0)
.subscribe({ t -> setupComposeMessageButtons(t) })
.subscribe({ t -> setupComposeMessageButtons(t) })
compositeDisposable.add(disposable)
}
......
......@@ -16,6 +16,7 @@ import chat.rocket.android.chatroom.viewmodel.BaseViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_pinned_messages.*
import javax.inject.Inject
......@@ -62,22 +63,30 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
presenter.loadPinnedMessages(chatRoomId)
}
override fun showLoading() = view_loading.setVisible(true)
override fun showLoading() {
ui { view_loading.setVisible(true) }
}
override fun hideLoading() = view_loading.setVisible(false)
override fun hideLoading() {
ui { view_loading.setVisible(false) }
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun showPinnedMessages(pinnedMessages: List<BaseViewModel<*>>) {
activity?.apply {
ui {
if (recycler_view_pinned.adapter == null) {
// TODO - add a better constructor for this case...
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, null, false)
......
......@@ -270,6 +270,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// TODO - Temporary stuff, remove when adding DB support
private suspend fun subscribeRoomUpdates() {
manager.addStatusChannel(stateChannel)
view.showConnectionState(client.state)
manager.addRoomsAndSubscriptionsChannel(subscriptionsChannel)
launch(CommonPool + strategy.jobs) {
for (message in subscriptionsChannel) {
......
......@@ -24,7 +24,6 @@ import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType
import chat.rocket.core.model.ChatRoom
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
......
......@@ -32,9 +32,11 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.NonCancellable.isActive
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
......@@ -101,8 +103,8 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_sort -> {
val dialogLayout = layoutInflater.inflate(R.layout.chatroom_sort_dialog, null)
val sortType = SharedPreferenceHelper.getInt(Constants.CHATROOM_SORT_TYPE_KEY, ChatRoomsSortOrder.ACTIVITY)
......@@ -154,50 +156,57 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
override suspend fun updateChatRooms(newDataSet: List<ChatRoom>) {
activity?.apply {
listJob?.cancel()
listJob = launch(UI) {
val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android/issues/5ac2916c36c7b235275ccccf
// TODO - fix this bug to re-enable DiffUtil
/*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await()*/
if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
adapter.notifyDataSetChanged()
//Set sections always after data set is updated
setSections()
}
listJob?.cancel()
listJob = ui {
val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android/issues/5ac2916c36c7b235275ccccf
// TODO - fix this bug to re-enable DiffUtil
/*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await()*/
if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
adapter.notifyDataSetChanged()
//Set sections always after data set is updated
setSections()
}
}
}
override fun showNoChatRoomsToDisplay() = text_no_data_to_display.setVisible(true)
override fun showNoChatRoomsToDisplay() {
ui { text_no_data_to_display.setVisible(true) }
}
override fun showLoading() = view_loading.setVisible(true)
override fun showLoading(){
ui { view_loading.setVisible(true) }
}
override fun hideLoading() {
if (view_loading != null) {
ui {
view_loading.setVisible(false)
}
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun showConnectionState(state: State) {
activity?.apply {
Timber.d("Got new state: $state")
ui {
connection_status_text.fadeIn()
handler.removeCallbacks(dismissStatus)
when (state) {
......@@ -221,22 +230,25 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
private fun setupToolbar() {
(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.title_chats)
(activity as AppCompatActivity?)?.supportActionBar?.title = getString(R.string.title_chats)
}
private fun setupRecyclerView() {
activity?.apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this,
ui {
recycler_view.layoutManager = LinearLayoutManager(it, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)))
recycler_view.itemAnimator = DefaultItemAnimator()
// TODO - use a ViewModel Mapper instead of using settings on the adapter
val baseAdapter = ChatRoomsAdapter(this,
settingsRepository.get(serverInteractor.get()!!), localRepository) { chatRoom -> presenter.loadChatRoom(chatRoom) }
val baseAdapter = ChatRoomsAdapter(it,
settingsRepository.get(serverInteractor.get()!!), localRepository) {
chatRoom -> presenter.loadChatRoom(chatRoom)
}
sectionedAdapter = SimpleSectionedRecyclerViewAdapter(this, R.layout.item_chatroom_header, R.id.text_chatroom_header, baseAdapter!!)
sectionedAdapter = SimpleSectionedRecyclerViewAdapter(it,
R.layout.item_chatroom_header, R.id.text_chatroom_header, baseAdapter)
recycler_view.adapter = sectionedAdapter
}
}
......
package chat.rocket.android.members.presentation
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.member.ui.newInstance
import chat.rocket.android.members.ui.newInstance
class MembersNavigator(internal val activity: ChatRoomActivity) {
......
package chat.rocket.android.member.ui
package chat.rocket.android.members.ui
import android.os.Bundle
import android.support.design.widget.BottomSheetDialogFragment
......
......@@ -19,6 +19,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModel
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.*
......@@ -70,7 +71,7 @@ class MembersFragment : Fragment(), MembersView {
}
override fun showMembers(dataSet: List<MemberViewModel>, total: Long) {
activity?.apply {
ui {
setupToolbar(total)
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
......@@ -84,8 +85,8 @@ class MembersFragment : Fragment(), MembersView {
} else {
adapter.appendData(dataSet)
}
if (this is ChatRoomActivity) {
this.showRoomTypeIcon(false)
if (it is ChatRoomActivity) {
it.showRoomTypeIcon(false)
}
}
}
......@@ -98,29 +99,37 @@ class MembersFragment : Fragment(), MembersView {
return super.onOptionsItemSelected(item)
}
override fun showLoading() = view_loading.setVisible(true)
override fun showLoading() {
ui { view_loading.setVisible(true) }
}
override fun hideLoading() = view_loading.setVisible(false)
override fun hideLoading() {
ui { view_loading.setVisible(false) }
}
override fun showMessage(resId: Int) {
showToast(resId)
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
showToast(message)
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun setupRecyclerView() {
activity?.apply {
ui {
recycler_view.layoutManager = linearLayoutManager
recycler_view.addItemDecoration(DividerItemDecoration(this))
recycler_view.addItemDecoration(DividerItemDecoration(it))
recycler_view.adapter = adapter
}
}
private fun setupToolbar(totalMembers: Long) {
(activity as ChatRoomActivity).setupToolbarTitle(getString(R.string.title_members, totalMembers))
(activity as ChatRoomActivity?)?.setupToolbarTitle(getString(R.string.title_members, totalMembers))
}
}
\ No newline at end of file
......@@ -15,7 +15,7 @@ import java.util.concurrent.CopyOnWriteArrayList
class ConnectionManager(internal val client: RocketChatClient) {
private val statusChannelList = CopyOnWriteArrayList<Channel<State>>()
private val statusChannel = Channel<State>()
private val statusChannel = Channel<State>(Channel.CONFLATED)
private var connectJob: Job? = null
private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>()
......@@ -67,7 +67,8 @@ class ConnectionManager(internal val client: RocketChatClient) {
}
for (channel in statusChannelList) {
channel.send(status)
Timber.d("Sending status: $status to $channel")
channel.offer(status)
}
}
}
......
......@@ -11,7 +11,10 @@ import chat.rocket.android.util.extensions.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.textContent
import android.support.v7.view.ActionMode
import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.fragment_password.*
import javax.inject.Inject
......@@ -19,6 +22,7 @@ import javax.inject.Inject
class PasswordFragment: Fragment(), PasswordView, android.support.v7.view.ActionMode.Callback {
@Inject lateinit var presenter: PasswordPresenter
private var actionMode: ActionMode? = null
private val disposables = CompositeDisposable()
companion object {
fun newInstance() = PasswordFragment()
......@@ -34,13 +38,20 @@ class PasswordFragment: Fragment(), PasswordView, android.support.v7.view.Action
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listenToChanges()
disposables.add(listenToChanges())
}
override fun onDestroyView() {
disposables.clear()
super.onDestroyView()
}
override fun hideLoading() {
layout_new_password.visibility = View.VISIBLE
layout_confirm_password.visibility = View.VISIBLE
view_loading.visibility = View.GONE
ui {
layout_new_password.visibility = View.VISIBLE
layout_confirm_password.visibility = View.VISIBLE
view_loading.visibility = View.GONE
}
}
override fun onActionItemClicked(mode: ActionMode, menuItem: MenuItem): Boolean {
......@@ -69,9 +80,11 @@ class PasswordFragment: Fragment(), PasswordView, android.support.v7.view.Action
}
override fun showLoading() {
layout_new_password.visibility = View.GONE
layout_confirm_password.visibility = View.GONE
view_loading.visibility = View.VISIBLE
ui {
layout_new_password.visibility = View.GONE
layout_confirm_password.visibility = View.GONE
view_loading.visibility = View.VISIBLE
}
}
override fun showPasswordFailsUpdateMessage(error: String?) {
......@@ -84,8 +97,9 @@ class PasswordFragment: Fragment(), PasswordView, android.support.v7.view.Action
private fun finishActionMode() = actionMode?.finish()
private fun listenToChanges() {
Observables.combineLatest(text_new_password.asObservable(), text_confirm_password.asObservable()).subscribe {
private fun listenToChanges(): Disposable {
return Observables.combineLatest(text_new_password.asObservable(),
text_confirm_password.asObservable()).subscribe {
val textPassword = text_new_password.textContent
val textConfirmPassword = text_confirm_password.textContent
......@@ -97,7 +111,9 @@ class PasswordFragment: Fragment(), PasswordView, android.support.v7.view.Action
}
private fun showToast(msg: String?) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show()
ui {
Toast.makeText(it, msg, Toast.LENGTH_LONG).show()
}
}
private fun startActionMode() {
......
package chat.rocket.android.util.extensions
import android.os.Looper
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
inline fun Fragment.ui(crossinline block: (activity: FragmentActivity) -> Unit): Job? {
// Checking first for activity and view saves us from some synchronyzed and thread local checks
if (activity != null && view != null) {
// If we already are running on the Main Thread (UI Thread), just go ahead and execute the block
return if (Looper.getMainLooper() == Looper.myLooper()) {
block(activity!!)
null
} else {
// Launch a Job on the UI context and check again if the activity and view are still valid
launch(UI) {
if (activity != null && view != null) {
block(activity!!)
}
}
}
}
return null
}
\ No newline at end of file
......@@ -10,14 +10,14 @@
android:paddingTop="@dimen/chat_item_top_and_bottom_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding">
<include
android:id="@+id/layout_avatar"
layout="@layout/avatar"
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
app:roundedCornerRadius="3dp"
android:layout_marginTop="6dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/text_chat_name"
......@@ -25,7 +25,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/layout_avatar"
app:layout_constraintStart_toEndOf="@id/image_avatar"
android:textDirection="locale"
tools:text="General"/>
......@@ -51,7 +51,7 @@
app:layout_constraintTop_toBottomOf="@id/text_chat_name"
app:layout_constraintEnd_toStartOf="@id/layout_unread_messages_badge"
android:textDirection="locale"
tools:text="You: Type something"/>
tools:text="You: Type something that is very big and need at least to lines, or maybe even more"/>
<include
android:id="@+id/layout_unread_messages_badge"
......
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