Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
7dcb3b46
Unverified
Commit
7dcb3b46
authored
Feb 28, 2018
by
Lucio Maciel
Committed by
GitHub
Feb 28, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'release/2.0.0-beta7' into feature/member-details
parents
13161ca1
1cb3f896
Changes
42
Show whitespace changes
Inline
Side-by-side
Showing
42 changed files
with
812 additions
and
308 deletions
+812
-308
build.gradle
app/build.gradle
+4
-2
AudioAttachmentViewHolder.kt
...ket/android/chatroom/adapter/AudioAttachmentViewHolder.kt
+5
-1
BaseViewHolder.kt
...va/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
+50
-1
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+33
-5
ImageAttachmentViewHolder.kt
...ket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
+10
-1
MessageViewHolder.kt
...chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
+5
-54
UrlPreviewViewHolder.kt
...t/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
+9
-1
VideoAttachmentViewHolder.kt
...ket/android/chatroom/adapter/VideoAttachmentViewHolder.kt
+5
-1
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+70
-59
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+3
-0
PinnedMessagesPresenter.kt
.../android/chatroom/presentation/PinnedMessagesPresenter.kt
+2
-3
ChatRoomActivity.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
+10
-0
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+37
-1
AudioAttachmentViewModel.kt
...et/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
+2
-0
BaseViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
+2
-0
ImageAttachmentViewModel.kt
...et/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
+2
-0
MessageViewModel.kt
...hat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
+2
-2
UrlPreviewViewModel.kt
.../rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
+2
-0
VideoAttachmentViewModel.kt
...et/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
+2
-0
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+49
-29
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+102
-85
ChatRoomsView.kt
...at/rocket/android/chatrooms/presentation/ChatRoomsView.kt
+3
-0
ChatRoomsAdapter.kt
...java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
+8
-1
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+45
-7
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+19
-1
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+12
-1
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+8
-0
SettingsRepository.kt
...a/chat/rocket/android/server/domain/SettingsRepository.kt
+18
-18
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+155
-0
ConnectionManagerFactory.kt
...ndroid/server/infraestructure/ConnectionManagerFactory.kt
+22
-0
RocketChatClientFactory.kt
...android/server/infraestructure/RocketChatClientFactory.kt
+1
-1
SharedPreferencesSettingsRepository.kt
...er/infraestructure/SharedPreferencesSettingsRepository.kt
+3
-3
Animation.kt
...ain/java/chat/rocket/android/util/extensions/Animation.kt
+47
-25
Ui.kt
app/src/main/java/chat/rocket/android/util/extensions/Ui.kt
+12
-1
emoji_popup_layout.xml
app/src/main/res/layout/emoji_popup_layout.xml
+1
-1
fragment_chat_room.xml
app/src/main/res/layout/fragment_chat_room.xml
+15
-0
fragment_chat_rooms.xml
app/src/main/res/layout/fragment_chat_rooms.xml
+15
-0
message_attachment.xml
app/src/main/res/layout/message_attachment.xml
+2
-2
message_url_preview.xml
app/src/main/res/layout/message_url_preview.xml
+2
-2
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+7
-0
strings.xml
app/src/main/res/values/strings.xml
+8
-0
dependencies.gradle
dependencies.gradle
+3
-0
No files found.
app/build.gradle
View file @
7dcb3b46
...
@@ -12,8 +12,8 @@ android {
...
@@ -12,8 +12,8 @@ android {
applicationId
"chat.rocket.android"
applicationId
"chat.rocket.android"
minSdkVersion
21
minSdkVersion
21
targetSdkVersion
versions
.
targetSdk
targetSdkVersion
versions
.
targetSdk
versionCode
10
09
versionCode
10
10
versionName
"2.0.0-dev
7
"
versionName
"2.0.0-dev
8
"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
multiDexEnabled
true
}
}
...
@@ -60,6 +60,8 @@ dependencies {
...
@@ -60,6 +60,8 @@ dependencies {
implementation
libraries
.
constraintLayout
implementation
libraries
.
constraintLayout
implementation
libraries
.
cardView
implementation
libraries
.
cardView
implementation
libraries
.
androidKtx
implementation
libraries
.
dagger
implementation
libraries
.
dagger
implementation
libraries
.
daggerSupport
implementation
libraries
.
daggerSupport
kapt
libraries
.
daggerProcessor
kapt
libraries
.
daggerProcessor
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/AudioAttachmentViewHolder.kt
View file @
7dcb3b46
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
AudioAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
AudioAttachmentViewModel
>(
itemView
)
{
class
AudioAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
AudioAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
init
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setVisible
(
false
)
image_attachment
.
setVisible
(
false
)
audio_video_attachment
.
setVisible
(
true
)
audio_video_attachment
.
setVisible
(
true
)
setupActionMenu
(
attachment_container
)
setupActionMenu
(
audio_video_attachment
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.support.v7.widget.RecyclerView
import
android.support.v7.widget.RecyclerView
import
android.view.MenuItem
import
android.view.View
import
android.view.View
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.viewmodel.BaseViewModel
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.isSystemMessage
import
ru.whalemare.sheetmenu.extension.inflate
import
ru.whalemare.sheetmenu.extension.toList
abstract
class
BaseViewHolder
<
T
>(
itemView
:
View
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
abstract
class
BaseViewHolder
<
T
:
BaseViewModel
<*>>(
itemView
:
View
,
private
val
listener
:
ActionsListener
)
:
RecyclerView
.
ViewHolder
(
itemView
),
MenuItem
.
OnMenuItemClickListener
{
var
data
:
T
?
=
null
var
data
:
T
?
=
null
init
{
setupActionMenu
(
itemView
)
}
fun
bind
(
data
:
T
)
{
fun
bind
(
data
:
T
)
{
this
.
data
=
data
this
.
data
=
data
bindViews
(
data
)
bindViews
(
data
)
}
}
abstract
fun
bindViews
(
data
:
T
)
abstract
fun
bindViews
(
data
:
T
)
interface
ActionsListener
{
fun
isActionsEnabled
():
Boolean
fun
onActionSelected
(
item
:
MenuItem
,
message
:
Message
)
}
val
longClickListener
=
{
view
:
View
->
if
(
data
?.
message
?.
isSystemMessage
()
==
false
)
{
val
menuItems
=
view
.
context
.
inflate
(
R
.
menu
.
message_actions
).
toList
()
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_menu_msg_pin_unpin
}
?.
apply
{
val
isPinned
=
data
?.
message
?.
pinned
?:
false
setTitle
(
if
(
isPinned
)
R
.
string
.
action_msg_unpin
else
R
.
string
.
action_msg_pin
)
isChecked
=
isPinned
}
val
adapter
=
ActionListAdapter
(
menuItems
,
this
@BaseViewHolder
)
BottomSheetMenu
(
adapter
).
show
(
view
.
context
)
}
true
}
internal
fun
setupActionMenu
(
view
:
View
)
{
if
(
listener
.
isActionsEnabled
())
{
view
.
setOnLongClickListener
(
longClickListener
)
}
}
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
{
data
?.
let
{
listener
.
onActionSelected
(
item
,
it
.
message
)
}
return
true
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.support.v7.widget.RecyclerView
import
android.support.v7.widget.RecyclerView
import
android.view.MenuItem
import
android.view.ViewGroup
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.viewmodel.*
import
chat.rocket.android.chatroom.viewmodel.*
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.core.model.Message
import
timber.log.Timber
import
timber.log.Timber
import
java.security.InvalidParameterException
import
java.security.InvalidParameterException
...
@@ -26,23 +28,23 @@ class ChatRoomAdapter(
...
@@ -26,23 +28,23 @@ class ChatRoomAdapter(
return
when
(
viewType
.
toViewType
())
{
return
when
(
viewType
.
toViewType
())
{
BaseViewModel
.
ViewType
.
MESSAGE
->
{
BaseViewModel
.
ViewType
.
MESSAGE
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message
)
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message
)
MessageViewHolder
(
view
,
roomName
,
roomType
,
presenter
,
enableActions
)
MessageViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
ImageAttachmentViewHolder
(
view
)
ImageAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
AudioAttachmentViewHolder
(
view
)
AudioAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
VideoAttachmentViewHolder
(
view
)
VideoAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
URL_PREVIEW
->
{
BaseViewModel
.
ViewType
.
URL_PREVIEW
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
UrlPreviewViewHolder
(
view
)
UrlPreviewViewHolder
(
view
,
actionsListener
)
}
}
else
->
{
else
->
{
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
...
@@ -108,4 +110,30 @@ class ChatRoomAdapter(
...
@@ -108,4 +110,30 @@ class ChatRoomAdapter(
notifyItemRangeRemoved
(
index
,
oldSize
-
newSize
)
notifyItemRangeRemoved
(
index
,
oldSize
-
newSize
)
}
}
}
}
val
actionsListener
=
object
:
BaseViewHolder
.
ActionsListener
{
override
fun
isActionsEnabled
():
Boolean
=
enableActions
override
fun
onActionSelected
(
item
:
MenuItem
,
message
:
Message
)
{
message
.
apply
{
when
(
item
.
itemId
)
{
R
.
id
.
action_menu_msg_delete
->
presenter
?.
deleteMessage
(
roomId
,
id
)
R
.
id
.
action_menu_msg_quote
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
false
)
R
.
id
.
action_menu_msg_reply
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
true
)
R
.
id
.
action_menu_msg_copy
->
presenter
?.
copyMessage
(
id
)
R
.
id
.
action_menu_msg_edit
->
presenter
?.
editMessage
(
roomId
,
id
,
message
.
message
)
R
.
id
.
action_menu_msg_pin_unpin
->
{
with
(
item
)
{
if
(!
isChecked
)
{
presenter
?.
pinMessage
(
id
)
}
else
{
presenter
?.
unpinMessage
(
id
)
}
}
}
else
->
TODO
(
"Not implemented"
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
View file @
7dcb3b46
...
@@ -5,7 +5,16 @@ import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
...
@@ -5,7 +5,16 @@ import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
import
com.stfalcon.frescoimageviewer.ImageViewer
import
com.stfalcon.frescoimageviewer.ImageViewer
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
ImageAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
)
{
class
ImageAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
with
(
itemView
)
{
setupActionMenu
(
attachment_container
)
setupActionMenu
(
image_attachment
)
}
}
override
fun
bindViews
(
data
:
ImageAttachmentViewModel
)
{
override
fun
bindViews
(
data
:
ImageAttachmentViewModel
)
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setImageURI
(
data
.
attachmentUrl
)
image_attachment
.
setImageURI
(
data
.
attachmentUrl
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.text.method.LinkMovementMethod
import
android.text.method.LinkMovementMethod
import
android.view.MenuItem
import
android.view.View
import
android.view.View
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import
chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
import
ru.whalemare.sheetmenu.extension.inflate
import
ru.whalemare.sheetmenu.extension.toList
class
MessageViewHolder
(
class
MessageViewHolder
(
itemView
:
View
,
itemView
:
View
,
private
val
roomType
:
String
,
listener
:
ActionsListener
private
val
roomName
:
String
,
)
:
BaseViewHolder
<
MessageViewModel
>(
itemView
,
listener
)
{
private
val
presenter
:
ChatRoomPresenter
?,
enableActions
:
Boolean
)
:
BaseViewHolder
<
MessageViewModel
>(
itemView
),
MenuItem
.
OnMenuItemClickListener
{
init
{
init
{
itemView
.
text_content
.
movementMethod
=
LinkMovementMethod
()
with
(
itemView
)
{
text_content
.
movementMethod
=
LinkMovementMethod
()
if
(
enableActions
)
{
setupActionMenu
(
text_content
)
itemView
.
setOnLongClickListener
{
if
(
data
?.
isSystemMessage
==
false
)
{
val
menuItems
=
it
.
context
.
inflate
(
R
.
menu
.
message_actions
).
toList
()
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_menu_msg_pin_unpin
}
?.
apply
{
val
isPinned
=
data
?.
isPinned
?:
false
setTitle
(
if
(
isPinned
)
R
.
string
.
action_msg_unpin
else
R
.
string
.
action_msg_pin
)
isChecked
=
isPinned
}
val
adapter
=
ActionListAdapter
(
menuItems
,
this
@MessageViewHolder
)
BottomSheetMenu
(
adapter
).
apply
{
}.
show
(
it
.
context
)
}
true
}
}
}
}
}
...
@@ -52,27 +26,4 @@ class MessageViewHolder(
...
@@ -52,27 +26,4 @@ class MessageViewHolder(
image_avatar
.
setImageURI
(
data
.
avatar
)
image_avatar
.
setImageURI
(
data
.
avatar
)
}
}
}
}
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
{
data
?.
rawData
?.
apply
{
when
(
item
.
itemId
)
{
R
.
id
.
action_menu_msg_delete
->
presenter
?.
deleteMessage
(
roomId
,
id
)
R
.
id
.
action_menu_msg_quote
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
false
)
R
.
id
.
action_menu_msg_reply
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
true
)
R
.
id
.
action_menu_msg_copy
->
presenter
?.
copyMessage
(
id
)
R
.
id
.
action_menu_msg_edit
->
presenter
?.
editMessage
(
roomId
,
id
,
message
)
R
.
id
.
action_menu_msg_pin_unpin
->
{
with
(
item
)
{
if
(!
isChecked
)
{
presenter
?.
pinMessage
(
id
)
}
else
{
presenter
?.
unpinMessage
(
id
)
}
}
}
else
->
TODO
(
"Not implemented"
)
}
}
return
true
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
View file @
7dcb3b46
...
@@ -8,7 +8,15 @@ import chat.rocket.android.util.extensions.content
...
@@ -8,7 +8,15 @@ import chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_url_preview.view.*
import
kotlinx.android.synthetic.main.message_url_preview.view.*
class
UrlPreviewViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
UrlPreviewViewModel
>(
itemView
)
{
class
UrlPreviewViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
UrlPreviewViewModel
>(
itemView
,
listener
)
{
init
{
with
(
itemView
)
{
setupActionMenu
(
url_preview_layout
)
}
}
override
fun
bindViews
(
data
:
UrlPreviewViewModel
)
{
override
fun
bindViews
(
data
:
UrlPreviewViewModel
)
{
with
(
itemView
)
{
with
(
itemView
)
{
if
(
data
.
thumbUrl
.
isNullOrEmpty
())
{
if
(
data
.
thumbUrl
.
isNullOrEmpty
())
{
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/VideoAttachmentViewHolder.kt
View file @
7dcb3b46
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
VideoAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
VideoAttachmentViewModel
>(
itemView
)
{
class
VideoAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
VideoAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
init
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setVisible
(
false
)
image_attachment
.
setVisible
(
false
)
audio_video_attachment
.
setVisible
(
true
)
audio_video_attachment
.
setVisible
(
true
)
setupActionMenu
(
attachment_container
)
setupActionMenu
(
audio_video_attachment
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
7dcb3b46
...
@@ -6,23 +6,23 @@ import chat.rocket.android.chatroom.domain.UriInteractor
...
@@ -6,23 +6,23 @@ import chat.rocket.android.chatroom.domain.UriInteractor
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.RoomType
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.State
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.realtime.connect
import
chat.rocket.core.internal.realtime.subscribeRoomMessages
import
chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.launch
import
org.threeten.bp.Instant
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -34,14 +34,22 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -34,14 +34,22 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private
val
permissions
:
GetPermissionsInteractor
,
private
val
permissions
:
GetPermissionsInteractor
,
private
val
uriInteractor
:
UriInteractor
,
private
val
uriInteractor
:
UriInteractor
,
private
val
messagesRepository
:
MessagesRepository
,
private
val
messagesRepository
:
MessagesRepository
,
factory
:
RocketChatClient
Factory
,
factory
:
ConnectionManager
Factory
,
private
val
mapper
:
ViewModelMapper
)
{
private
val
mapper
:
ViewModelMapper
)
{
private
val
client
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
var
subId
:
String
?
=
null
private
val
manager
=
factory
.
create
(
currentServer
)
private
val
client
=
manager
.
client
private
var
settings
:
Map
<
String
,
Value
<
Any
>>
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
!!
private
var
settings
:
Map
<
String
,
Value
<
Any
>>
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
!!
private
val
messagesChannel
=
Channel
<
Message
>()
private
var
chatRoomId
:
String
?
=
null
private
var
chatRoomType
:
String
?
=
null
private
val
stateChannel
=
Channel
<
State
>()
private
val
stateChannel
=
Channel
<
State
>()
private
var
lastState
=
manager
.
state
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
this
.
chatRoomId
=
chatRoomId
this
.
chatRoomType
=
chatRoomType
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
view
.
showLoading
()
view
.
showLoading
()
try
{
try
{
...
@@ -56,10 +64,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -56,10 +64,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val
messagesViewModels
=
mapper
.
map
(
messages
)
val
messagesViewModels
=
mapper
.
map
(
messages
)
view
.
showMessages
(
messagesViewModels
)
view
.
showMessages
(
messagesViewModels
)
// Subscribe after getting the first page of messages from REST
if
(
offset
==
0L
)
{
subscribeMessages
(
chatRoomId
)
subscribeMessages
(
chatRoomId
)
}
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
ex
.
printStackTrace
()
ex
.
printStackTrace
()
ex
.
message
?.
let
{
ex
.
message
?.
let
{
...
@@ -70,6 +75,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -70,6 +75,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
finally
{
}
finally
{
view
.
hideLoading
()
view
.
hideLoading
()
}
}
if
(
offset
==
0L
)
{
subscribeState
()
}
}
}
}
}
...
@@ -132,7 +141,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -132,7 +141,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
}
fun
markRoomAsRead
(
roomId
:
String
)
{
private
fun
markRoomAsRead
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
try
{
try
{
client
.
markAsRead
(
roomId
)
client
.
markAsRead
(
roomId
)
...
@@ -143,57 +152,70 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -143,57 +152,70 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
}
private
fun
subscribeMessages
(
roomId
:
String
)
{
private
fun
subscribeState
()
{
client
.
addStateChannel
(
stateChannel
)
Timber
.
d
(
"Subscribing to Status changes"
)
lastState
=
manager
.
state
manager
.
addStatusChannel
(
stateChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
status
in
stateChannel
)
{
for
(
state
in
stateChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
Timber
.
d
(
"Got new state: $state - last: $lastState"
)
when
(
status
)
{
if
(
state
!=
lastState
)
{
State
.
Authenticating
->
Timber
.
d
(
"Authenticating"
)
launch
(
UI
)
{
State
.
Connected
->
{
view
.
showConnectionState
(
state
)
Timber
.
d
(
"Connected"
)
}
subId
=
client
.
subscribeRoomMessages
(
roomId
)
{
Timber
.
d
(
"subscribe messages for $roomId: $it"
)
if
(
state
is
State
.
Connected
)
{
loadMissingMessages
()
}
}
}
}
lastState
=
state
}
}
}
}
Timber
.
d
(
"Done on statusChannel"
)
}
}
when
(
client
.
state
)
{
private
fun
subscribeMessages
(
roomId
:
String
)
{
State
.
Connected
->
{
manager
.
subscribeRoomMessages
(
roomId
,
messagesChannel
)
Timber
.
d
(
"Already connected"
)
subId
=
client
.
subscribeRoomMessages
(
roomId
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
Timber
.
d
(
"subscribe messages for $roomId: $it"
)
for
(
message
in
messagesChannel
)
{
Timber
.
d
(
"New message for room ${message.roomId}"
)
updateMessage
(
message
)
}
}
}
}
else
->
client
.
connect
()
}
}
private
fun
loadMissingMessages
()
{
launch
(
parent
=
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
chatRoomType
!=
null
)
{
val
roomType
=
roomTypeOf
(
chatRoomType
!!
)
val
lastMessage
=
messagesRepository
.
getByRoomId
(
chatRoomId
!!
).
sortedByDescending
{
it
.
timestamp
}.
first
()
val
instant
=
Instant
.
ofEpochMilli
(
lastMessage
.
timestamp
)
val
messages
=
client
.
history
(
chatRoomId
!!
,
roomType
,
count
=
50
,
oldest
=
instant
.
toString
())
Timber
.
d
(
"History: $messages"
)
if
(
messages
.
result
.
isNotEmpty
())
{
val
models
=
mapper
.
map
(
messages
.
result
)
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
listenMessages
(
roomId
)
view
.
showNewMessage
(
models
)
}
}
// TODO - when we have a proper service, we won't need to take care of connection, just
if
(
messages
.
result
.
size
==
50
)
{
// subscribe and listen...
// we loade at least count messages, try one more to fetch more messages
/*launchUI(strategy) {
loadMissingMessages
()
subId = client.subscribeRoomMessages(roomId) {
Timber.d("subscribe messages for $roomId: $it")
}
}
listenMessages(roomId)
}*/
}
}
fun
unsubscribeMessages
()
{
launch
(
CommonPool
)
{
client
.
removeStateChannel
(
stateChannel
)
subId
?.
let
{
subscriptionId
->
client
.
unsubscribe
(
subscriptionId
)
}
}
}
}
}
}
fun
unsubscribeMessages
(
chatRoomId
:
String
)
{
manager
.
removeStatusChannel
(
stateChannel
)
manager
.
unsubscribeRoomMessages
(
chatRoomId
)
}
/**
/**
* Delete the message with the given id.
* Delete the message with the given id.
*
*
...
@@ -320,17 +342,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -320,17 +342,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toMembersList
(
chatRoomId
,
chatRoomType
)
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toMembersList
(
chatRoomId
,
chatRoomType
)
private
suspend
fun
listenMessages
(
roomId
:
String
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
messagesChannel
)
{
if
(
message
.
roomId
!=
roomId
)
{
Timber
.
d
(
"Ignoring message for room ${message.roomId}, expecting $roomId"
)
}
updateMessage
(
message
)
}
}
}
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
)
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
)
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
7dcb3b46
...
@@ -4,6 +4,7 @@ import android.net.Uri
...
@@ -4,6 +4,7 @@ import android.net.Uri
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.State
interface
ChatRoomView
:
LoadingView
,
MessageView
{
interface
ChatRoomView
:
LoadingView
,
MessageView
{
...
@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView {
...
@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView {
fun
clearMessageComposition
()
fun
clearMessageComposition
()
fun
showInvalidFileSize
(
fileSize
:
Int
,
maxFileSize
:
Int
)
fun
showInvalidFileSize
(
fileSize
:
Int
,
maxFileSize
:
Int
)
fun
showConnectionState
(
state
:
State
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/PinnedMessagesPresenter.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.presentation
package
chat.rocket.android.chatroom.presentation
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.GetChatRoomsInteractor
import
chat.rocket.android.server.domain.GetChatRoomsInteractor
...
@@ -12,6 +11,7 @@ import chat.rocket.common.RocketChatException
...
@@ -12,6 +11,7 @@ import chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.rest.getRoomPinnedMessages
import
chat.rocket.core.internal.rest.getRoomPinnedMessages
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.isSystemMessage
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -42,8 +42,7 @@ class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessag
...
@@ -42,8 +42,7 @@ class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessag
val
pinnedMessages
=
val
pinnedMessages
=
client
.
getRoomPinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
client
.
getRoomPinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
pinnedMessagesListOffset
=
pinnedMessages
.
offset
.
toInt
()
pinnedMessagesListOffset
=
pinnedMessages
.
offset
.
toInt
()
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
)
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
.
filterNot
{
it
.
isSystemMessage
()
})
.
filter
{
it
is
MessageViewModel
}.
filterNot
{
(
it
as
MessageViewModel
).
isSystemMessage
}
view
.
showPinnedMessages
(
messageList
)
view
.
showPinnedMessages
(
messageList
)
view
.
hideLoading
()
view
.
hideLoading
()
}.
ifNull
{
}.
ifNull
{
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
View file @
7dcb3b46
...
@@ -6,6 +6,8 @@ import android.os.Bundle
...
@@ -6,6 +6,8 @@ import android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.util.extensions.addFragment
import
chat.rocket.android.util.extensions.addFragment
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.android.util.extensions.textContent
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjection
...
@@ -32,6 +34,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
...
@@ -32,6 +34,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class
ChatRoomActivity
:
AppCompatActivity
(),
HasSupportFragmentInjector
{
class
ChatRoomActivity
:
AppCompatActivity
(),
HasSupportFragmentInjector
{
@Inject
lateinit
var
fragmentDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Fragment
>
@Inject
lateinit
var
fragmentDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Fragment
>
// TODO - workaround for now... We will move to a single activity
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
managerFactory
:
ConnectionManagerFactory
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomType
:
String
private
lateinit
var
chatRoomType
:
String
...
@@ -42,6 +49,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
...
@@ -42,6 +49,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_chat_room
)
setContentView
(
R
.
layout
.
activity_chat_room
)
// Workaround for when we are coming to the app via the recents app and the app was killed.
managerFactory
.
create
(
serverInteractor
.
get
()
!!
).
connect
()
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
requireNotNull
(
chatRoomId
)
{
"no chat_room_id provided in Intent extras"
}
requireNotNull
(
chatRoomId
)
{
"no chat_room_id provided in Intent extras"
}
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
7dcb3b46
...
@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText
...
@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText
import
chat.rocket.android.widget.emoji.Emoji
import
chat.rocket.android.widget.emoji.Emoji
import
chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import
chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.core.internal.realtime.State
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.CompositeDisposable
import
kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.fragment_chat_room.*
...
@@ -114,7 +115,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -114,7 +115,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
}
}
override
fun
onDestroyView
()
{
override
fun
onDestroyView
()
{
presenter
.
unsubscribeMessages
()
presenter
.
unsubscribeMessages
(
chatRoomId
)
handler
.
removeCallbacksAndMessages
(
null
)
handler
.
removeCallbacksAndMessages
(
null
)
unsubscribeTextMessage
()
unsubscribeTextMessage
()
super
.
onDestroyView
()
super
.
onDestroyView
()
...
@@ -225,6 +226,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -225,6 +226,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
.
title
=
username
actionSnackbar
.
title
=
username
actionSnackbar
.
text
=
quotedMessage
actionSnackbar
.
text
=
quotedMessage
actionSnackbar
.
show
()
actionSnackbar
.
show
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
if
(!
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
}
}
}
}
}
}
...
@@ -252,6 +259,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -252,6 +259,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
.
show
()
actionSnackbar
.
show
()
text_message
.
textContent
=
text
text_message
.
textContent
=
text
editingMessageId
=
messageId
editingMessageId
=
messageId
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
}
}
}
}
...
@@ -288,6 +296,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -288,6 +296,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
showMessage
(
getString
(
R
.
string
.
max_file_size_exceeded
,
fileSize
,
maxFileSize
))
showMessage
(
getString
(
R
.
string
.
max_file_size_exceeded
,
fileSize
,
maxFileSize
))
}
}
override
fun
showConnectionState
(
state
:
State
)
{
activity
?.
apply
{
connection_status_text
.
fadeIn
()
handler
.
removeCallbacks
(
dismissStatus
)
when
(
state
)
{
is
State
.
Connected
->
{
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connected
)
handler
.
postDelayed
(
dismissStatus
,
2000
)
}
is
State
.
Disconnected
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
}
}
}
private
val
dismissStatus
=
{
connection_status_text
.
fadeOut
()
}
private
fun
setupRecyclerView
()
{
private
fun
setupRecyclerView
()
{
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
...
@@ -320,6 +350,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -320,6 +350,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
emojiKeyboardPopup
.
listener
=
this
emojiKeyboardPopup
.
listener
=
this
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
override
fun
onKeyboardOpened
()
{
override
fun
onKeyboardOpened
()
{
if
(
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
}
}
}
}
override
fun
onKeyboardClosed
()
{
override
fun
onKeyboardClosed
()
{
...
@@ -395,6 +430,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -395,6 +430,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
=
ActionSnackbar
.
make
(
message_list_container
,
parser
=
parser
)
actionSnackbar
=
ActionSnackbar
.
make
(
message_list_container
,
parser
=
parser
)
actionSnackbar
.
cancelView
.
setOnClickListener
({
actionSnackbar
.
cancelView
.
setOnClickListener
({
clearMessageComposition
()
clearMessageComposition
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
})
})
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.AudioAttachment
import
chat.rocket.core.model.attachment.AudioAttachment
data class
AudioAttachmentViewModel
(
data class
AudioAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
AudioAttachment
,
override
val
rawData
:
AudioAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.core.model.Message
import
java.security.InvalidParameterException
import
java.security.InvalidParameterException
interface
BaseViewModel
<
out
T
>
{
interface
BaseViewModel
<
out
T
>
{
val
message
:
Message
val
rawData
:
T
val
rawData
:
T
val
messageId
:
String
val
messageId
:
String
val
viewType
:
Int
val
viewType
:
Int
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.ImageAttachment
import
chat.rocket.core.model.attachment.ImageAttachment
data class
ImageAttachmentViewModel
(
data class
ImageAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
ImageAttachment
,
override
val
rawData
:
ImageAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
View file @
7dcb3b46
...
@@ -4,14 +4,14 @@ import chat.rocket.android.R
...
@@ -4,14 +4,14 @@ import chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
data class
MessageViewModel
(
data class
MessageViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
Message
,
override
val
rawData
:
Message
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
avatar
:
String
,
override
val
avatar
:
String
,
override
val
time
:
CharSequence
,
override
val
time
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
isPinned
:
Boolean
,
override
val
isPinned
:
Boolean
val
isSystemMessage
:
Boolean
)
:
BaseMessageViewModel
<
Message
>
{
)
:
BaseMessageViewModel
<
Message
>
{
override
val
viewType
:
Int
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.url.Url
import
chat.rocket.core.model.url.Url
data class
UrlPreviewViewModel
(
data class
UrlPreviewViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
Url
,
override
val
rawData
:
Url
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
val
title
:
CharSequence
?,
val
title
:
CharSequence
?,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
View file @
7dcb3b46
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.VideoAttachment
import
chat.rocket.core.model.attachment.VideoAttachment
data class
VideoAttachmentViewModel
(
data class
VideoAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
VideoAttachment
,
override
val
rawData
:
VideoAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
7dcb3b46
...
@@ -19,11 +19,13 @@ import chat.rocket.core.model.Message
...
@@ -19,11 +19,13 @@ import chat.rocket.core.model.Message
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.attachment.*
import
chat.rocket.core.model.attachment.*
import
chat.rocket.core.model.isSystemMessage
import
chat.rocket.core.model.url.Url
import
chat.rocket.core.model.url.Url
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.experimental.withContext
import
okhttp3.HttpUrl
import
okhttp3.HttpUrl
import
timber.log.Timber
import
timber.log.Timber
import
java.security.InvalidParameterException
import
javax.inject.Inject
import
javax.inject.Inject
class
ViewModelMapper
@Inject
constructor
(
private
val
context
:
Context
,
class
ViewModelMapper
@Inject
constructor
(
private
val
context
:
Context
,
...
@@ -81,7 +83,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -81,7 +83,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val
title
=
url
.
meta
?.
title
val
title
=
url
.
meta
?.
title
val
description
=
url
.
meta
?.
description
val
description
=
url
.
meta
?.
description
return
UrlPreviewViewModel
(
url
,
message
.
id
,
title
,
hostname
,
description
,
thumb
)
return
UrlPreviewViewModel
(
message
,
url
,
message
.
id
,
title
,
hostname
,
description
,
thumb
)
}
}
private
fun
mapAttachment
(
message
:
Message
,
attachment
:
Attachment
):
BaseViewModel
<
*
>?
{
private
fun
mapAttachment
(
message
:
Message
,
attachment
:
Attachment
):
BaseViewModel
<
*
>?
{
...
@@ -96,12 +98,12 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -96,12 +98,12 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val
attachmentTitle
=
attachment
.
title
val
attachmentTitle
=
attachment
.
title
val
id
=
"${message.id}_${attachment.titleLink}"
.
hashCode
().
toLong
()
val
id
=
"${message.id}_${attachment.titleLink}"
.
hashCode
().
toLong
()
return
when
(
attachment
)
{
return
when
(
attachment
)
{
is
ImageAttachment
->
ImageAttachmentViewModel
(
attachment
,
message
.
id
,
attachmentUrl
,
is
ImageAttachment
->
ImageAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentTitle
?:
""
,
id
)
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
VideoAttachment
->
VideoAttachmentViewModel
(
attachment
,
message
.
id
,
is
VideoAttachment
->
VideoAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
AudioAttachment
->
AudioAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
AudioAttachment
->
AudioAttachmentViewModel
(
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
else
->
null
else
->
null
}
}
}
}
...
@@ -143,13 +145,16 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -143,13 +145,16 @@ class ViewModelMapper @Inject constructor(private val context: Context,
}
}
val
content
=
getContent
(
context
,
message
,
quote
)
val
content
=
getContent
(
context
,
message
,
quote
)
MessageViewModel
(
rawData
=
message
,
messageId
=
message
.
id
,
MessageViewModel
(
message
=
message
,
rawData
=
message
,
messageId
=
message
.
id
,
avatar
=
avatar
!!
,
time
=
time
,
senderName
=
sender
,
avatar
=
avatar
!!
,
time
=
time
,
senderName
=
sender
,
content
=
content
.
first
,
isPinned
=
message
.
pinned
,
content
=
content
,
isPinned
=
message
.
pinned
)
isSystemMessage
=
content
.
second
)
}
}
private
fun
getSenderName
(
message
:
Message
):
CharSequence
{
private
fun
getSenderName
(
message
:
Message
):
CharSequence
{
if
(!
message
.
senderAlias
.
isNullOrEmpty
())
{
return
message
.
senderAlias
!!
}
val
username
=
message
.
sender
?.
username
val
username
=
message
.
sender
?.
username
val
realName
=
message
.
sender
?.
name
val
realName
=
message
.
sender
?.
name
val
senderName
=
if
(
settings
.
useRealName
())
realName
else
username
val
senderName
=
if
(
settings
.
useRealName
())
realName
else
username
...
@@ -157,6 +162,10 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -157,6 +162,10 @@ class ViewModelMapper @Inject constructor(private val context: Context,
}
}
private
fun
getUserAvatar
(
message
:
Message
):
String
?
{
private
fun
getUserAvatar
(
message
:
Message
):
String
?
{
message
.
avatar
?.
let
{
return
it
// Always give preference for overridden avatar from message
}
val
username
=
message
.
sender
?.
username
?:
"?"
val
username
=
message
.
sender
?.
username
?:
"?"
return
baseUrl
?.
let
{
return
baseUrl
?.
let
{
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
...
@@ -171,28 +180,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -171,28 +180,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
Timber
.
d
(
"Will quote message Id: $msgIdToQuote"
)
Timber
.
d
(
"Will quote message Id: $msgIdToQuote"
)
return
if
(
msgIdToQuote
!=
null
)
messagesRepository
.
getById
(
msgIdToQuote
)
else
null
return
if
(
msgIdToQuote
!=
null
)
messagesRepository
.
getById
(
msgIdToQuote
)
else
null
}
}
return
null
return
null
}
}
private
suspend
fun
getContent
(
context
:
Context
,
message
:
Message
,
quote
:
Message
?):
Pair
<
CharSequence
,
Boolean
>
{
private
suspend
fun
getContent
(
context
:
Context
,
message
:
Message
,
quote
:
Message
?):
CharSequence
{
var
systemMessage
=
true
return
when
(
message
.
isSystemMessage
())
{
val
content
=
when
(
message
.
type
)
{
true
->
getSystemMessage
(
message
,
context
)
//TODO: Add implementation for Welcome type.
false
->
getNormalMessage
(
message
,
quote
)
is
MessageType
.
MessageRemoved
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_removed
))
is
MessageType
.
UserJoined
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_joined_channel
))
is
MessageType
.
UserLeft
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_left
))
is
MessageType
.
UserAdded
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_added_by
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
RoomNameChanged
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_room_name_changed
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
UserRemoved
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_removed_by
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
MessagePinned
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_pinned
))
else
->
{
systemMessage
=
false
getNormalMessage
(
message
,
quote
)
}
}
}
return
Pair
(
content
,
systemMessage
)
}
}
private
suspend
fun
getNormalMessage
(
message
:
Message
,
quote
:
Message
?):
CharSequence
{
private
suspend
fun
getNormalMessage
(
message
:
Message
,
quote
:
Message
?):
CharSequence
{
...
@@ -204,7 +199,32 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -204,7 +199,32 @@ class ViewModelMapper @Inject constructor(private val context: Context,
return
parser
.
renderMarkdown
(
message
.
message
,
quoteViewModel
,
currentUsername
)
return
parser
.
renderMarkdown
(
message
.
message
,
quoteViewModel
,
currentUsername
)
}
}
private
fun
getSystemMessage
(
content
:
String
):
CharSequence
{
private
fun
getSystemMessage
(
message
:
Message
,
context
:
Context
):
CharSequence
{
val
content
=
when
(
message
.
type
)
{
//TODO: Add implementation for Welcome type.
is
MessageType
.
MessageRemoved
->
context
.
getString
(
R
.
string
.
message_removed
)
is
MessageType
.
UserJoined
->
context
.
getString
(
R
.
string
.
message_user_joined_channel
)
is
MessageType
.
UserLeft
->
context
.
getString
(
R
.
string
.
message_user_left
)
is
MessageType
.
UserAdded
->
context
.
getString
(
R
.
string
.
message_user_added_by
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
RoomNameChanged
->
context
.
getString
(
R
.
string
.
message_room_name_changed
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
UserRemoved
->
context
.
getString
(
R
.
string
.
message_user_removed_by
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
MessagePinned
->
{
val
attachment
=
message
.
attachments
?.
get
(
0
)
val
pinnedSystemMessage
=
context
.
getString
(
R
.
string
.
message_pinned
)
if
(
attachment
!=
null
&&
attachment
is
MessageAttachment
)
{
return
SpannableStringBuilder
(
pinnedSystemMessage
)
.
apply
{
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
length
,
0
)
setSpan
(
ForegroundColorSpan
(
Color
.
GRAY
),
0
,
length
,
0
)
}
.
append
(
quoteMessage
(
attachment
.
author
!!
,
attachment
.
text
!!
,
attachment
.
timestamp
!!
))
}
return
pinnedSystemMessage
}
else
->
{
throw
InvalidParameterException
(
"Invalid message type: ${message.type}"
)
}
}
//isSystemMessage = true
//isSystemMessage = true
val
spannableMsg
=
SpannableStringBuilder
(
content
)
val
spannableMsg
=
SpannableStringBuilder
(
content
)
spannableMsg
.
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
spannableMsg
.
length
,
spannableMsg
.
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
spannableMsg
.
length
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
7dcb3b46
package
chat.rocket.android.chatrooms.presentation
package
chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.
GetChatRoomsInteractor
import
chat.rocket.android.server.domain.
*
import
chat.rocket.android.server.
domain.GetCurrentServerInteracto
r
import
chat.rocket.android.server.
infraestructure.ConnectionManage
r
import
chat.rocket.android.server.
domain.RefreshSettingsInteractor
import
chat.rocket.android.server.
infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.
domain.SaveChatRoomsInteractor
import
chat.rocket.android.server.
infraestructure.chatRooms
import
chat.rocket.android.server.infraestructure.
RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.
state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.RoomType
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.realtime.*
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.internal.realtime.StreamMessage
import
chat.rocket.core.internal.realtime.Type
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.Room
import
chat.rocket.core.model.Room
import
kotlinx.coroutines.experimental.*
import
kotlinx.coroutines.experimental.*
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.channels.Channel
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -26,31 +29,47 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -26,31 +29,47 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
factory
:
RocketChatClientFactory
)
{
settingsRepository
:
SettingsRepository
,
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
factory
:
ConnectionManagerFactory
)
{
private
val
manager
:
ConnectionManager
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
var
reloadJob
:
Deferred
<
List
<
ChatRoom
>>?
=
null
private
var
reloadJob
:
Deferred
<
List
<
ChatRoom
>>?
=
null
private
val
settings
=
settingsRepository
.
get
(
currentServer
)
!!
private
val
subscriptionsChannel
=
Channel
<
StreamMessage
<
BaseRoom
>>()
private
val
stateChannel
=
Channel
<
State
>()
private
val
stateChannel
=
Channel
<
State
>()
private
var
lastState
=
manager
.
state
fun
loadChatRooms
()
{
fun
loadChatRooms
()
{
refreshSettingsInteractor
.
refreshAsync
(
currentServer
)
refreshSettingsInteractor
.
refreshAsync
(
currentServer
)
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
view
.
showLoading
()
view
.
showLoading
()
subscribeStatusChange
()
try
{
try
{
view
.
updateChatRooms
(
loadRooms
())
view
.
updateChatRooms
(
loadRooms
())
subscribeRoomUpdates
()
}
catch
(
e
:
RocketChatException
)
{
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
Timber
.
e
(
e
)
view
.
showMessage
(
e
.
message
!!
)
view
.
showMessage
(
e
.
message
!!
)
}
finally
{
}
finally
{
view
.
hideLoading
()
view
.
hideLoading
()
}
}
subscribeRoomUpdates
()
}
}
}
}
fun
loadChatRoom
(
chatRoom
:
ChatRoom
)
=
navigator
.
toChatRoom
(
chatRoom
.
id
,
chatRoom
.
name
,
fun
loadChatRoom
(
chatRoom
:
ChatRoom
)
{
val
roomName
=
if
(
chatRoom
.
type
is
RoomType
.
DirectMessage
&&
chatRoom
.
fullName
!=
null
&&
settings
.
useRealName
())
{
chatRoom
.
fullName
!!
}
else
{
chatRoom
.
name
}
navigator
.
toChatRoom
(
chatRoom
.
id
,
roomName
,
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
)
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
)
}
/**
/**
* Gets a [ChatRoom] list from local repository.
* Gets a [ChatRoom] list from local repository.
...
@@ -65,8 +84,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -65,8 +84,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private
suspend
fun
loadRooms
():
List
<
ChatRoom
>
{
private
suspend
fun
loadRooms
():
List
<
ChatRoom
>
{
val
chatRooms
=
client
.
chatRooms
().
update
val
chatRooms
=
manager
.
chatRooms
().
update
val
sortedRooms
=
sortRooms
(
chatRooms
)
val
sortedRooms
=
sortRooms
(
chatRooms
)
Timber
.
d
(
"Loaded rooms: ${sortedRooms.size}"
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
return
sortedRooms
return
sortedRooms
}
}
...
@@ -77,6 +97,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -77,6 +97,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private
fun
updateRooms
()
{
private
fun
updateRooms
()
{
Timber
.
d
(
"Updating Rooms"
)
launch
{
launch
{
view
.
updateChatRooms
(
getChatRoomsInteractor
.
get
(
currentServer
))
view
.
updateChatRooms
(
getChatRoomsInteractor
.
get
(
currentServer
))
}
}
...
@@ -92,52 +113,43 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -92,52 +113,43 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
}
}
// TODO - Temporary stuff, remove when adding DB support
private
suspend
fun
subscribeStatusChange
()
{
private
suspend
fun
subscribeRoomUpdates
()
{
lastState
=
manager
.
state
client
.
addStateChannel
(
stateChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
status
in
stateChannel
)
{
for
(
state
in
stateChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
Timber
.
d
(
"Got new state: $state - last: $lastState"
)
when
(
status
)
{
if
(
state
!=
lastState
)
{
State
.
Authenticating
->
Timber
.
d
(
"Authenticating"
)
launch
(
UI
)
{
State
.
Connected
->
{
view
.
showConnectionState
(
state
)
Timber
.
d
(
"Connected"
)
client
.
subscribeSubscriptions
{
Timber
.
d
(
"subscriptions: $it"
)
}
client
.
subscribeRooms
{
Timber
.
d
(
"rooms: $it"
)
}
}
}
if
(
state
is
State
.
Connected
)
{
reloadRooms
()
updateRooms
()
}
}
}
}
Timber
.
d
(
"Done on statusChannel"
)
lastState
=
state
}
}
when
(
client
.
state
)
{
State
.
Connected
->
{
Timber
.
d
(
"Already connected"
)
}
}
else
->
client
.
connect
()
}
}
// TODO - Temporary stuff, remove when adding DB support
private
suspend
fun
subscribeRoomUpdates
()
{
manager
.
addStatusChannel
(
stateChannel
)
manager
.
addRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
room
sChannel
)
{
for
(
message
in
subscription
sChannel
)
{
Timber
.
d
(
"Got message: $message"
)
Timber
.
d
(
"Got message: $message"
)
updateRoom
(
message
)
when
(
message
.
data
)
{
}
is
Room
->
updateRoom
(
message
as
StreamMessage
<
Room
>)
is
Subscription
->
updateSubscription
(
message
as
StreamMessage
<
Subscription
>)
}
}
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
subscriptionsChannel
)
{
Timber
.
d
(
"Got message: $message"
)
updateSubscription
(
message
)
}
}
}
}
}
}
private
fun
updateRoom
(
message
:
StreamMessage
<
Room
>)
{
private
suspend
fun
updateRoom
(
message
:
StreamMessage
<
Room
>)
{
launchUI
(
strategy
)
{
Timber
.
d
(
"Update Room: ${message.type} - ${message.data.id} - ${message.data.name}"
)
when
(
message
.
type
)
{
when
(
message
.
type
)
{
Type
.
Removed
->
{
Type
.
Removed
->
{
removeRoom
(
message
.
data
.
id
)
removeRoom
(
message
.
data
.
id
)
...
@@ -154,10 +166,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -154,10 +166,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
updateRooms
()
updateRooms
()
}
}
}
private
fun
updateSubscription
(
message
:
StreamMessage
<
Subscription
>)
{
private
suspend
fun
updateSubscription
(
message
:
StreamMessage
<
Subscription
>)
{
launchUI
(
strategy
)
{
Timber
.
d
(
"Update Subscription: ${message.type} - ${message.data.id} - ${message.data.name}"
)
when
(
message
.
type
)
{
when
(
message
.
type
)
{
Type
.
Removed
->
{
Type
.
Removed
->
{
removeRoom
(
message
.
data
.
roomId
)
removeRoom
(
message
.
data
.
roomId
)
...
@@ -174,22 +185,26 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -174,22 +185,26 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
updateRooms
()
updateRooms
()
}
}
}
private
suspend
fun
reloadRooms
()
{
private
suspend
fun
reloadRooms
()
{
Timber
.
d
(
"realoadRooms()"
)
Timber
.
d
(
"realoadRooms()"
)
reloadJob
?.
cancel
()
reloadJob
?.
cancel
()
try
{
reloadJob
=
async
(
CommonPool
+
strategy
.
jobs
)
{
reloadJob
=
async
(
CommonPool
+
strategy
.
jobs
)
{
delay
(
1000
)
delay
(
1000
)
Timber
.
d
(
"reloading rooms after wait"
)
Timber
.
d
(
"reloading rooms after wait"
)
loadRooms
()
loadRooms
()
}
}
reloadJob
?.
await
()
reloadJob
?.
await
()
}
catch
(
ex
:
Exception
)
{
ex
.
printStackTrace
()
}
}
}
// Update a ChatRoom with a Room information
// Update a ChatRoom with a Room information
private
fun
updateRoom
(
room
:
Room
)
{
private
fun
updateRoom
(
room
:
Room
)
{
Timber
.
d
(
"Updating Room: ${room.id} - ${room.name}"
)
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
room
.
id
}
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
room
.
id
}
chatRoom
?.
apply
{
chatRoom
?.
apply
{
...
@@ -220,6 +235,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -220,6 +235,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Subscription information
// Update a ChatRoom with a Subscription information
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
Timber
.
d
(
"Updating subscrition: ${subscription.id} - ${subscription.name}"
)
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
chatRoom
?.
apply
{
chatRoom
?.
apply
{
...
@@ -251,6 +267,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -251,6 +267,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
fun
removeRoom
(
id
:
String
,
private
fun
removeRoom
(
id
:
String
,
chatRooms
:
MutableList
<
ChatRoom
>
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
())
{
chatRooms
:
MutableList
<
ChatRoom
>
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
())
{
Timber
.
d
(
"Removing ROOM: $id"
)
synchronized
(
this
)
{
synchronized
(
this
)
{
chatRooms
.
removeAll
{
chatRoom
->
chatRoom
.
id
==
id
}
chatRooms
.
removeAll
{
chatRoom
->
chatRoom
.
id
==
id
}
}
}
...
@@ -258,7 +275,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -258,7 +275,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
fun
disconnect
()
{
fun
disconnect
()
{
client
.
removeState
Channel
(
stateChannel
)
manager
.
removeStatus
Channel
(
stateChannel
)
client
.
disconnect
(
)
manager
.
removeRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsView.kt
View file @
7dcb3b46
...
@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
...
@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
interface
ChatRoomsView
:
LoadingView
,
MessageView
{
interface
ChatRoomsView
:
LoadingView
,
MessageView
{
...
@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView {
...
@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView {
* Shows no chat rooms to display.
* Shows no chat rooms to display.
*/
*/
fun
showNoChatRoomsToDisplay
()
fun
showNoChatRoomsToDisplay
()
fun
showConnectionState
(
state
:
State
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
View file @
7dcb3b46
...
@@ -9,6 +9,8 @@ import android.view.ViewGroup
...
@@ -9,6 +9,8 @@ import android.view.ViewGroup
import
android.widget.TextView
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
...
@@ -21,6 +23,7 @@ import kotlinx.android.synthetic.main.item_chat.view.*
...
@@ -21,6 +23,7 @@ import kotlinx.android.synthetic.main.item_chat.view.*
import
kotlinx.android.synthetic.main.unread_messages_badge.view.*
import
kotlinx.android.synthetic.main.unread_messages_badge.view.*
class
ChatRoomsAdapter
(
private
val
context
:
Context
,
class
ChatRoomsAdapter
(
private
val
context
:
Context
,
private
val
settings
:
PublicSettings
,
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
...
@@ -69,8 +72,12 @@ class ChatRoomsAdapter(private val context: Context,
...
@@ -69,8 +72,12 @@ class ChatRoomsAdapter(private val context: Context,
}
}
private
fun
bindName
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
private
fun
bindName
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
if
(
chatRoom
.
type
is
RoomType
.
DirectMessage
&&
settings
.
useRealName
())
{
textView
.
content
=
chatRoom
.
fullName
?:
chatRoom
.
name
}
else
{
textView
.
content
=
chatRoom
.
name
textView
.
content
=
chatRoom
.
name
}
}
}
private
fun
bindLastMessageDateTime
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
private
fun
bindLastMessageDateTime
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
val
lastMessage
=
chatRoom
.
lastMessage
val
lastMessage
=
chatRoom
.
lastMessage
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
7dcb3b46
package
chat.rocket.android.chatrooms.ui
package
chat.rocket.android.chatrooms.ui
import
android.os.Bundle
import
android.os.Bundle
import
android.os.Handler
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.util.DiffUtil
import
android.support.v7.util.DiffUtil
...
@@ -11,14 +12,16 @@ import android.view.*
...
@@ -11,14 +12,16 @@ import android.view.*
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
import
chat.rocket.android.
util.extensions.inflate
import
chat.rocket.android.
server.domain.GetCurrentServerInteractor
import
chat.rocket.android.
util.extensions.setVisible
import
chat.rocket.android.
server.domain.SettingsRepository
import
chat.rocket.android.util.extensions.
showToast
import
chat.rocket.android.util.extensions.
*
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_chat_rooms.*
import
kotlinx.android.synthetic.main.fragment_chat_rooms.*
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.launch
...
@@ -26,7 +29,12 @@ import javax.inject.Inject
...
@@ -26,7 +29,12 @@ import javax.inject.Inject
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
settingsRepository
:
SettingsRepository
private
var
searchView
:
SearchView
?
=
null
private
var
searchView
:
SearchView
?
=
null
private
val
handler
=
Handler
()
private
var
listJob
:
Job
?
=
null
companion
object
{
companion
object
{
fun
newInstance
()
=
ChatRoomsFragment
()
fun
newInstance
()
=
ChatRoomsFragment
()
...
@@ -39,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -39,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
}
override
fun
onDestroy
()
{
override
fun
onDestroy
()
{
handler
.
removeCallbacks
(
dismissStatus
)
presenter
.
disconnect
()
presenter
.
disconnect
()
super
.
onDestroy
()
super
.
onDestroy
()
}
}
...
@@ -72,17 +81,22 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -72,17 +81,22 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override
suspend
fun
updateChatRooms
(
newDataSet
:
List
<
ChatRoom
>)
{
override
suspend
fun
updateChatRooms
(
newDataSet
:
List
<
ChatRoom
>)
{
activity
.
apply
{
activity
.
apply
{
launch
(
UI
)
{
listJob
?.
cancel
()
listJob
=
launch
(
UI
)
{
val
adapter
=
recycler_view
.
adapter
as
ChatRoomsAdapter
val
adapter
=
recycler_view
.
adapter
as
ChatRoomsAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android.dev/issues/5a90d4718cb3c2fa63b3f557?time=last-seven-days
// TODO - fix this bug to reenable DiffUtil
val
diff
=
async
(
CommonPool
)
{
val
diff
=
async
(
CommonPool
)
{
DiffUtil
.
calculateDiff
(
RoomsDiffCallback
(
adapter
.
dataSet
,
newDataSet
))
DiffUtil
.
calculateDiff
(
RoomsDiffCallback
(
adapter
.
dataSet
,
newDataSet
))
}.
await
()
}.
await
()
if
(
isActive
)
{
adapter
.
updateRooms
(
newDataSet
)
adapter
.
updateRooms
(
newDataSet
)
diff
.
dispatchUpdatesTo
(
adapter
)
diff
.
dispatchUpdatesTo
(
adapter
)
}
}
}
}
}
}
}
override
fun
showNoChatRoomsToDisplay
()
=
text_no_data_to_display
.
setVisible
(
true
)
override
fun
showNoChatRoomsToDisplay
()
=
text_no_data_to_display
.
setVisible
(
true
)
...
@@ -96,6 +110,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -96,6 +110,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showConnectionState
(
state
:
State
)
{
activity
?.
apply
{
connection_status_text
.
fadeIn
()
handler
.
removeCallbacks
(
dismissStatus
)
when
(
state
)
{
is
State
.
Connected
->
{
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connected
)
handler
.
postDelayed
(
dismissStatus
,
2000
)
}
is
State
.
Disconnected
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
}
}
}
private
val
dismissStatus
=
{
connection_status_text
.
fadeOut
()
}
private
fun
setupToolbar
()
{
private
fun
setupToolbar
()
{
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
getString
(
R
.
string
.
title_chats
)
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
getString
(
R
.
string
.
title_chats
)
}
}
...
@@ -107,7 +143,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -107,7 +143,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_start
),
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_start
),
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_end
)))
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_end
)))
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
)
{
chatRoom
->
// TODO - use a ViewModel Mapper instead of using settings on the adapter
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
,
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
)
!!
)
{
chatRoom
->
presenter
.
loadChatRoom
(
chatRoom
)
presenter
.
loadChatRoom
(
chatRoom
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
View file @
7dcb3b46
...
@@ -13,11 +13,15 @@ import android.support.v4.content.res.ResourcesCompat
...
@@ -13,11 +13,15 @@ import android.support.v4.content.res.ResourcesCompat
import
android.text.Layout
import
android.text.Layout
import
android.text.Spannable
import
android.text.Spannable
import
android.text.Spanned
import
android.text.Spanned
import
android.text.TextUtils
import
android.text.style.*
import
android.text.style.*
import
android.util.Patterns
import
android.util.Patterns
import
android.view.View
import
android.view.View
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiRepository
import
chat.rocket.android.widget.emoji.EmojiTypefaceSpan
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.BlockQuote
import
org.commonmark.node.BlockQuote
import
org.commonmark.node.Text
import
org.commonmark.node.Text
...
@@ -47,7 +51,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -47,7 +51,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
*/
*/
fun
renderMarkdown
(
text
:
String
,
quote
:
MessageViewModel
?
=
null
,
selfUsername
:
String
?
=
null
):
CharSequence
{
fun
renderMarkdown
(
text
:
String
,
quote
:
MessageViewModel
?
=
null
,
selfUsername
:
String
?
=
null
):
CharSequence
{
val
builder
=
SpannableBuilder
()
val
builder
=
SpannableBuilder
()
val
content
=
text
val
content
=
EmojiRepository
.
shortnameToUnicode
(
text
,
true
)
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
parentNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
parentNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
quote
?.
apply
{
quote
?.
apply
{
...
@@ -58,6 +62,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -58,6 +62,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
quoteNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
quoteNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
}
}
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
EmojiVisitor
(
builder
))
val
result
=
builder
.
text
()
val
result
=
builder
.
text
()
applySpans
(
result
,
selfUsername
)
applySpans
(
result
,
selfUsername
)
...
@@ -139,6 +144,19 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -139,6 +144,19 @@ class MessageParser @Inject constructor(val context: Application, private val co
}
}
}
}
class
EmojiVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
override
fun
visit
(
text
:
Text
)
{
val
spannable
=
EmojiParser
.
parse
(
text
.
literal
)
if
(
spannable
is
Spanned
)
{
val
spans
=
spannable
.
getSpans
(
0
,
spannable
.
length
,
EmojiTypefaceSpan
::
class
.
java
)
spans
.
forEach
{
builder
.
setSpan
(
it
,
spannable
.
getSpanStart
(
it
),
spannable
.
getSpanEnd
(
it
),
0
)
}
}
visitChildren
(
text
)
}
}
class
LinkVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
class
LinkVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
override
fun
visit
(
text
:
Text
)
{
override
fun
visit
(
text
:
Text
)
{
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
7dcb3b46
...
@@ -2,6 +2,7 @@ package chat.rocket.android.main.presentation
...
@@ -2,6 +2,7 @@ package chat.rocket.android.main.presentation
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.RocketChatClient
...
@@ -13,9 +14,11 @@ import javax.inject.Inject
...
@@ -13,9 +14,11 @@ import javax.inject.Inject
class
MainPresenter
@Inject
constructor
(
private
val
navigator
:
MainNavigator
,
class
MainPresenter
@Inject
constructor
(
private
val
navigator
:
MainNavigator
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
localRepository
:
LocalRepository
,
private
val
localRepository
:
LocalRepository
,
managerFactory
:
ConnectionManagerFactory
,
factory
:
RocketChatClientFactory
)
{
factory
:
RocketChatClientFactory
)
{
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
manager
=
managerFactory
.
create
(
currentServer
)
private
val
client
:
RocketChatClient
=
factory
.
create
(
currentServer
)
fun
toChatList
()
=
navigator
.
toChatList
()
fun
toChatList
()
=
navigator
.
toChatList
()
...
@@ -49,4 +52,12 @@ class MainPresenter @Inject constructor(private val navigator: MainNavigator,
...
@@ -49,4 +52,12 @@ class MainPresenter @Inject constructor(private val navigator: MainNavigator,
}
}
localRepository
.
clearAllFromServer
(
currentServer
)
localRepository
.
clearAllFromServer
(
currentServer
)
}
}
fun
connect
()
{
manager
.
connect
()
}
fun
disconnect
()
{
manager
.
disconnect
()
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
View file @
7dcb3b46
...
@@ -29,6 +29,7 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
...
@@ -29,6 +29,7 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
setContentView
(
R
.
layout
.
activity_main
)
presenter
.
connect
()
setupToolbar
()
setupToolbar
()
setupNavigationView
()
setupNavigationView
()
}
}
...
@@ -41,6 +42,13 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
...
@@ -41,6 +42,13 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
}
}
}
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
if
(
isFinishing
)
{
presenter
.
disconnect
()
}
}
override
fun
onLogout
()
{
override
fun
onLogout
()
{
finish
()
finish
()
val
intent
=
Intent
(
this
,
AuthenticationActivity
::
class
.
java
)
val
intent
=
Intent
(
this
,
AuthenticationActivity
::
class
.
java
)
...
...
app/src/main/java/chat/rocket/android/server/domain/SettingsRepository.kt
View file @
7dcb3b46
...
@@ -48,30 +48,30 @@ const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
...
@@ -48,30 +48,30 @@ const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
* If you need to access a Setting, add a const val key above, add it to the filter on
* If you need to access a Setting, add a const val key above, add it to the filter on
* ServerPresenter.kt and a extension function to access it
* ServerPresenter.kt and a extension function to access it
*/
*/
fun
Map
<
String
,
Value
<
Any
>>
.
googleEnabled
():
Boolean
=
this
[
ACCOUNT_GOOGLE
]
?.
value
==
true
fun
PublicSettings
.
googleEnabled
():
Boolean
=
this
[
ACCOUNT_GOOGLE
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
facebookEnabled
():
Boolean
=
this
[
ACCOUNT_FACEBOOK
]
?.
value
==
true
fun
PublicSettings
.
facebookEnabled
():
Boolean
=
this
[
ACCOUNT_FACEBOOK
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
githubEnabled
():
Boolean
=
this
[
ACCOUNT_GITHUB
]
?.
value
==
true
fun
PublicSettings
.
githubEnabled
():
Boolean
=
this
[
ACCOUNT_GITHUB
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
linkedinEnabled
():
Boolean
=
this
[
ACCOUNT_LINKEDIN
]
?.
value
==
true
fun
PublicSettings
.
linkedinEnabled
():
Boolean
=
this
[
ACCOUNT_LINKEDIN
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
meteorEnabled
():
Boolean
=
this
[
ACCOUNT_METEOR
]
?.
value
==
true
fun
PublicSettings
.
meteorEnabled
():
Boolean
=
this
[
ACCOUNT_METEOR
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
twitterEnabled
():
Boolean
=
this
[
ACCOUNT_TWITTER
]
?.
value
==
true
fun
PublicSettings
.
twitterEnabled
():
Boolean
=
this
[
ACCOUNT_TWITTER
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
gitlabEnabled
():
Boolean
=
this
[
ACCOUNT_GITLAB
]
?.
value
==
true
fun
PublicSettings
.
gitlabEnabled
():
Boolean
=
this
[
ACCOUNT_GITLAB
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
wordpressEnabled
():
Boolean
=
this
[
ACCOUNT_WORDPRESS
]
?.
value
==
true
fun
PublicSettings
.
wordpressEnabled
():
Boolean
=
this
[
ACCOUNT_WORDPRESS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
useRealName
():
Boolean
=
this
[
USE_REALNAME
]
?.
value
==
true
fun
PublicSettings
.
useRealName
():
Boolean
=
this
[
USE_REALNAME
]
?.
value
==
true
// Message settings
// Message settings
fun
Map
<
String
,
Value
<
Any
>>
.
showDeletedStatus
():
Boolean
=
this
[
SHOW_DELETED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
showDeletedStatus
():
Boolean
=
this
[
SHOW_DELETED_STATUS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
showEditedStatus
():
Boolean
=
this
[
SHOW_EDITED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
showEditedStatus
():
Boolean
=
this
[
SHOW_EDITED_STATUS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessagePinning
():
Boolean
=
this
[
ALLOW_MESSAGE_PINNING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessagePinning
():
Boolean
=
this
[
ALLOW_MESSAGE_PINNING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessageEditing
():
Boolean
=
this
[
ALLOW_MESSAGE_EDITING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageEditing
():
Boolean
=
this
[
ALLOW_MESSAGE_EDITING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessageDeleting
():
Boolean
=
this
[
ALLOW_MESSAGE_DELETING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageDeleting
():
Boolean
=
this
[
ALLOW_MESSAGE_DELETING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
registrationEnabled
():
Boolean
{
fun
PublicSettings
.
registrationEnabled
():
Boolean
{
val
value
=
this
[
ACCOUNT_REGISTRATION
]
val
value
=
this
[
ACCOUNT_REGISTRATION
]
return
value
?.
value
==
"Public"
return
value
?.
value
==
"Public"
}
}
fun
Map
<
String
,
Value
<
Any
>>
.
uploadMimeTypeFilter
():
Array
<
String
>
{
fun
PublicSettings
.
uploadMimeTypeFilter
():
Array
<
String
>
{
val
values
=
this
[
UPLOAD_WHITELIST_MIMETYPES
]
?.
value
val
values
=
this
[
UPLOAD_WHITELIST_MIMETYPES
]
?.
value
values
?.
let
{
it
as
String
}
?.
split
(
","
)
?.
let
{
values
?.
let
{
it
as
String
}
?.
split
(
","
)
?.
let
{
return
it
.
mapToTypedArray
{
it
.
trim
()
}
return
it
.
mapToTypedArray
{
it
.
trim
()
}
...
@@ -80,8 +80,8 @@ fun Map<String, Value<Any>>.uploadMimeTypeFilter(): Array<String> {
...
@@ -80,8 +80,8 @@ fun Map<String, Value<Any>>.uploadMimeTypeFilter(): Array<String> {
return
arrayOf
(
"*/*"
)
return
arrayOf
(
"*/*"
)
}
}
fun
Map
<
String
,
Value
<
Any
>>
.
uploadMaxFileSize
():
Int
{
fun
PublicSettings
.
uploadMaxFileSize
():
Int
{
return
this
[
UPLOAD_MAX_FILE_SIZE
]
?.
value
?.
let
{
it
as
Int
}
?:
Int
.
MAX_VALUE
return
this
[
UPLOAD_MAX_FILE_SIZE
]
?.
value
?.
let
{
it
as
Int
}
?:
Int
.
MAX_VALUE
}
}
fun
Map
<
String
,
Value
<
Any
>>.
baseUrl
()
:
String
?
=
this
[
SITE_URL
]
?.
value
as
String
fun
PublicSettings
.
baseUrl
():
String
?
=
this
[
SITE_URL
]
?.
value
as
String
\ No newline at end of file
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
0 → 100644
View file @
7dcb3b46
package
chat.rocket.android.server.infraestructure
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.realtime.*
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.model.Message
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
timber.log.Timber
class
ConnectionManager
(
internal
val
client
:
RocketChatClient
)
{
private
val
statusChannelList
=
ArrayList
<
Channel
<
State
>>()
private
val
statusChannel
=
Channel
<
State
>()
private
var
connectJob
:
Job
?
=
null
private
val
roomAndSubscriptionChannels
=
ArrayList
<
Channel
<
StreamMessage
<
BaseRoom
>>>()
private
val
roomMessagesChannels
=
LinkedHashMap
<
String
,
Channel
<
Message
>>()
private
val
subscriptionIdMap
=
HashMap
<
String
,
String
>()
private
var
subscriptionId
:
String
?
=
null
private
var
roomsId
:
String
?
=
null
fun
connect
()
{
if
(
connectJob
?.
isActive
==
true
&&
(
state
!
is
State
.
Disconnected
))
{
Timber
.
d
(
"Already connected, just returning..."
)
return
}
// cleanup first
client
.
removeStateChannel
(
statusChannel
)
client
.
disconnect
()
connectJob
?.
cancel
()
// Connect and setup
client
.
addStateChannel
(
statusChannel
)
connectJob
=
launch
{
for
(
status
in
statusChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
when
(
status
)
{
is
State
.
Connected
->
{
client
.
subscribeSubscriptions
{
_
,
id
->
Timber
.
d
(
"Subscribed to subscriptions: $id"
)
subscriptionId
=
id
}
client
.
subscribeRooms
{
_
,
id
->
Timber
.
d
(
"Subscribed to rooms: $id"
)
roomsId
=
id
}
resubscribeRooms
()
}
is
State
.
Waiting
->
{
Timber
.
d
(
"Connection in: ${status.seconds}"
)
}
}
for
(
channel
in
statusChannelList
)
{
channel
.
send
(
status
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
room
in
client
.
roomsChannel
)
{
Timber
.
d
(
"GOT Room streamed"
)
for
(
channel
in
roomAndSubscriptionChannels
)
{
channel
.
send
(
room
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
subscription
in
client
.
subscriptionsChannel
)
{
Timber
.
d
(
"GOT Subscription streamed"
)
for
(
channel
in
roomAndSubscriptionChannels
)
{
channel
.
send
(
subscription
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
message
in
client
.
messagesChannel
)
{
Timber
.
d
(
"Received new Message for room ${message.roomId}"
)
val
channel
=
roomMessagesChannels
[
message
.
roomId
]
channel
?.
send
(
message
)
}
}
client
.
connect
()
// Broadcast initial state...
val
state
=
client
.
state
for
(
channel
in
statusChannelList
)
{
channel
.
offer
(
state
)
}
}
private
fun
resubscribeRooms
()
{
roomMessagesChannels
.
toList
().
map
{
(
roomId
,
channel
)
->
client
.
subscribeRoomMessages
(
roomId
)
{
_
,
id
->
Timber
.
d
(
"Subscribed to $roomId: $id"
)
subscriptionIdMap
[
roomId
]
=
id
}
}
}
fun
disconnect
()
{
Timber
.
d
(
"ConnectionManager DISCONNECT"
)
client
.
removeStateChannel
(
statusChannel
)
client
.
disconnect
()
connectJob
?.
cancel
()
}
fun
addStatusChannel
(
channel
:
Channel
<
State
>)
=
statusChannelList
.
add
(
channel
)
fun
removeStatusChannel
(
channel
:
Channel
<
State
>)
=
statusChannelList
.
remove
(
channel
)
fun
addRoomsAndSubscriptionsChannel
(
channel
:
Channel
<
StreamMessage
<
BaseRoom
>>)
=
roomAndSubscriptionChannels
.
add
(
channel
)
fun
removeRoomsAndSubscriptionsChannel
(
channel
:
Channel
<
StreamMessage
<
BaseRoom
>>)
=
roomAndSubscriptionChannels
.
remove
(
channel
)
fun
subscribeRoomMessages
(
roomId
:
String
,
channel
:
Channel
<
Message
>)
{
val
oldSub
=
roomMessagesChannels
.
put
(
roomId
,
channel
)
if
(
oldSub
!=
null
)
{
Timber
.
d
(
"Room $roomId already subscribed..."
)
return
}
if
(
client
.
state
is
State
.
Connected
)
{
client
.
subscribeRoomMessages
(
roomId
)
{
_
,
id
->
Timber
.
d
(
"Subscribed to $roomId: $id"
)
subscriptionIdMap
[
roomId
]
=
id
}
}
}
fun
unsubscribeRoomMessages
(
roomId
:
String
)
{
val
sub
=
roomMessagesChannels
.
remove
(
roomId
)
if
(
sub
!=
null
)
{
val
id
=
subscriptionIdMap
.
remove
(
roomId
)
id
?.
let
{
client
.
unsubscribe
(
it
)
}
}
}
}
suspend
fun
ConnectionManager
.
chatRooms
(
timestamp
:
Long
=
0
,
filterCustom
:
Boolean
=
true
)
=
client
.
chatRooms
(
timestamp
,
filterCustom
)
val
ConnectionManager
.
state
:
State
get
()
=
client
.
state
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManagerFactory.kt
0 → 100644
View file @
7dcb3b46
package
chat.rocket.android.server.infraestructure
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Singleton
@Singleton
class
ConnectionManagerFactory
@Inject
constructor
(
private
val
factory
:
RocketChatClientFactory
)
{
private
val
cache
=
HashMap
<
String
,
ConnectionManager
>()
fun
create
(
url
:
String
):
ConnectionManager
{
cache
[
url
]
?.
let
{
Timber
.
d
(
"Returning CACHED Manager for: $url"
)
return
it
}
Timber
.
d
(
"Returning FRESH Manager for: $url"
)
val
manager
=
ConnectionManager
(
factory
.
create
(
url
))
cache
[
url
]
=
manager
return
manager
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/RocketChatClientFactory.kt
View file @
7dcb3b46
...
@@ -28,7 +28,7 @@ class RocketChatClientFactory @Inject constructor(val okHttpClient: OkHttpClient
...
@@ -28,7 +28,7 @@ class RocketChatClientFactory @Inject constructor(val okHttpClient: OkHttpClient
}
}
Timber
.
d
(
"Returning NEW client for: $url"
)
Timber
.
d
(
"Returning NEW client for: $url"
)
cache
.
put
(
url
,
client
)
cache
[
url
]
=
client
return
client
return
client
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/SharedPreferencesSettingsRepository.kt
View file @
7dcb3b46
...
@@ -2,19 +2,19 @@ package chat.rocket.android.server.infraestructure
...
@@ -2,19 +2,19 @@ package chat.rocket.android.server.infraestructure
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
import
chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.core.internal.SettingsAdapter
import
chat.rocket.core.internal.SettingsAdapter
import
chat.rocket.core.model.Value
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
private
val
adapter
=
SettingsAdapter
().
lenient
()
private
val
adapter
=
SettingsAdapter
().
lenient
()
override
fun
save
(
url
:
String
,
settings
:
Map
<
String
,
Value
<
Any
>>
)
{
override
fun
save
(
url
:
String
,
settings
:
PublicSettings
)
{
localRepository
.
save
(
"$SETTINGS_KEY$url"
,
adapter
.
toJson
(
settings
))
localRepository
.
save
(
"$SETTINGS_KEY$url"
,
adapter
.
toJson
(
settings
))
}
}
override
fun
get
(
url
:
String
):
Map
<
String
,
Value
<
Any
>>
?
{
override
fun
get
(
url
:
String
):
PublicSettings
?
{
val
settings
=
localRepository
.
get
(
"$SETTINGS_KEY$url"
)
val
settings
=
localRepository
.
get
(
"$SETTINGS_KEY$url"
)
settings
?.
let
{
settings
?.
let
{
return
adapter
.
fromJson
(
it
)
return
adapter
.
fromJson
(
it
)
...
...
app/src/main/java/chat/rocket/android/util/extensions/Animation.kt
View file @
7dcb3b46
...
@@ -2,7 +2,6 @@ package chat.rocket.android.util.extensions
...
@@ -2,7 +2,6 @@ package chat.rocket.android.util.extensions
import
android.view.View
import
android.view.View
import
android.view.ViewAnimationUtils
import
android.view.ViewAnimationUtils
import
android.view.animation.AccelerateInterpolator
import
android.view.animation.DecelerateInterpolator
import
android.view.animation.DecelerateInterpolator
fun
View
.
rotateBy
(
value
:
Float
,
duration
:
Long
=
100
)
{
fun
View
.
rotateBy
(
value
:
Float
,
duration
:
Long
=
100
)
{
...
@@ -12,36 +11,59 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
...
@@ -12,36 +11,59 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
.
start
()
.
start
()
}
}
fun
View
.
fadeIn
(
startValue
:
Float
,
finishValue
:
Float
,
duration
:
Long
=
200
)
{
fun
View
.
fadeIn
(
start
:
Float
=
0f
,
end
:
Float
=
1f
,
duration
:
Long
=
200
)
{
animate
()
// already at end alpha, just set visible and return
.
alpha
(
startValue
)
if
(
alpha
==
end
)
{
setVisible
(
true
)
return
}
val
animation
=
animate
()
.
alpha
(
end
)
.
setDuration
(
duration
)
.
setDuration
(
duration
)
.
setInterpolator
(
DecelerateInterpolator
())
.
setInterpolator
(
DecelerateInterpolator
())
.
withEndAction
(
{
if
(
start
!=
alpha
)
{
animate
()
animate
()
.
alpha
(
finishValue
)
.
alpha
(
start
)
.
setDuration
(
duration
)
.
setDuration
(
duration
/
2
)
// half the time, so the entire animation runs on duration
.
setInterpolator
(
AccelerateInterpolator
()).
start
()
.
setInterpolator
(
DecelerateInterpolator
())
}).
start
()
.
withEndAction
{
animation
.
setDuration
(
duration
/
2
).
start
()
}.
start
()
}
else
{
animation
.
start
()
}
setVisible
(
true
)
setVisible
(
true
)
}
}
fun
View
.
fadeOut
(
startValue
:
Float
,
finishValue
:
Float
,
duration
:
Long
=
200
)
{
fun
View
.
fadeOut
(
start
:
Float
=
1f
,
end
:
Float
=
0f
,
duration
:
Long
=
200
)
{
animate
()
if
(
alpha
==
end
)
{
.
alpha
(
startValue
)
setVisible
(
false
)
return
}
val
animation
=
animate
()
.
alpha
(
end
)
.
setDuration
(
duration
)
.
setDuration
(
duration
)
.
setInterpolator
(
DecelerateInterpolator
())
.
setInterpolator
(
DecelerateInterpolator
())
.
withEndAction
({
.
withEndAction
{
animate
()
.
alpha
(
finishValue
)
.
setDuration
(
duration
)
.
setInterpolator
(
AccelerateInterpolator
()).
start
()
}).
start
()
setVisible
(
false
)
setVisible
(
false
)
}
if
(
start
!=
alpha
)
{
animate
()
.
alpha
(
start
)
.
setDuration
(
duration
/
2
)
// half the time, so the entire animation runs on duration
.
setInterpolator
(
DecelerateInterpolator
())
.
withEndAction
{
animation
.
setDuration
(
duration
/
2
).
start
()
}.
start
()
}
else
{
animation
.
start
()
}
}
}
fun
View
.
circularRevealOrUnreveal
(
centerX
:
Int
,
centerY
:
Int
,
startRadius
:
Float
,
endRadius
:
Float
,
duration
:
Long
=
200
)
{
fun
View
.
circularRevealOrUnreveal
(
centerX
:
Int
,
centerY
:
Int
,
startRadius
:
Float
,
endRadius
:
Float
,
duration
:
Long
=
200
)
{
val
anim
=
ViewAnimationUtils
.
createCircularReveal
(
this
,
centerX
,
centerY
,
startRadius
,
endRadius
)
val
anim
=
ViewAnimationUtils
.
createCircularReveal
(
this
,
centerX
,
centerY
,
startRadius
,
endRadius
)
anim
.
duration
=
duration
anim
.
duration
=
duration
...
...
app/src/main/java/chat/rocket/android/util/extensions/Ui.kt
View file @
7dcb3b46
...
@@ -6,6 +6,8 @@ import android.support.annotation.LayoutRes
...
@@ -6,6 +6,8 @@ import android.support.annotation.LayoutRes
import
android.support.annotation.StringRes
import
android.support.annotation.StringRes
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
...
@@ -55,3 +57,12 @@ fun Activity.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) = To
...
@@ -55,3 +57,12 @@ fun Activity.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) = To
fun
Fragment
.
showToast
(
@StringRes
resource
:
Int
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
showToast
(
getString
(
resource
),
duration
)
fun
Fragment
.
showToast
(
@StringRes
resource
:
Int
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
showToast
(
getString
(
resource
),
duration
)
fun
Fragment
.
showToast
(
message
:
String
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
activity
!!
.
showToast
(
message
,
duration
)
fun
Fragment
.
showToast
(
message
:
String
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
activity
!!
.
showToast
(
message
,
duration
)
fun
RecyclerView
.
isAtBottom
():
Boolean
{
val
manager
:
RecyclerView
.
LayoutManager
?
=
layoutManager
if
(
manager
is
LinearLayoutManager
)
{
return
manager
.
findFirstVisibleItemPosition
()
==
0
}
return
false
// or true??? we can't determine the first visible item.
}
\ No newline at end of file
app/src/main/res/layout/emoji_popup_layout.xml
View file @
7dcb3b46
...
@@ -60,6 +60,7 @@
...
@@ -60,6 +60,7 @@
android:clickable=
"true"
android:clickable=
"true"
android:focusable=
"true"
android:focusable=
"true"
android:padding=
"8dp"
android:padding=
"8dp"
android:visibility=
"invisible"
android:src=
"@drawable/ic_search_gray_24px"
/>
android:src=
"@drawable/ic_search_gray_24px"
/>
<ImageView
<ImageView
...
@@ -75,5 +76,4 @@
...
@@ -75,5 +76,4 @@
android:src=
"@drawable/ic_backspace_gray_24dp"
/>
android:src=
"@drawable/ic_backspace_gray_24dp"
/>
</RelativeLayout>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_chat_room.xml
View file @
7dcb3b46
...
@@ -53,4 +53,19 @@
...
@@ -53,4 +53,19 @@
android:layout_margin=
"5dp"
android:layout_margin=
"5dp"
android:visibility=
"gone"
/>
android:visibility=
"gone"
/>
<TextView
android:id=
"@+id/connection_status_text"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:background=
"@color/colorPrimary"
android:elevation=
"4dp"
android:textColor=
"@color/white"
android:gravity=
"center"
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:visibility=
"gone"
android:alpha=
"0"
tools:alpha=
"1"
tools:visibility=
"visible"
tools:text=
"connected"
/>
</RelativeLayout>
</RelativeLayout>
app/src/main/res/layout/fragment_chat_rooms.xml
View file @
7dcb3b46
...
@@ -29,4 +29,19 @@
...
@@ -29,4 +29,19 @@
android:visibility=
"gone"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/connection_status_text"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:background=
"@color/colorPrimary"
android:elevation=
"4dp"
android:textColor=
"@color/white"
android:gravity=
"center"
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:visibility=
"gone"
android:alpha=
"0"
tools:alpha=
"1"
tools:visibility=
"visible"
tools:text=
"connected"
/>
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
app/src/main/res/layout/message_attachment.xml
View file @
7dcb3b46
...
@@ -5,8 +5,8 @@
...
@@ -5,8 +5,8 @@
android:id=
"@+id/attachment_container"
android:id=
"@+id/attachment_container"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:
layout_margin
Start=
"72dp"
android:
padding
Start=
"72dp"
android:
layout_margin
End=
"@dimen/screen_edge_left_and_right_margins"
android:
padding
End=
"@dimen/screen_edge_left_and_right_margins"
android:orientation=
"vertical"
>
android:orientation=
"vertical"
>
<com.facebook.drawee.view.SimpleDraweeView
<com.facebook.drawee.view.SimpleDraweeView
...
...
app/src/main/res/layout/message_url_preview.xml
View file @
7dcb3b46
...
@@ -6,8 +6,8 @@
...
@@ -6,8 +6,8 @@
android:id=
"@+id/url_preview_layout"
android:id=
"@+id/url_preview_layout"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:
layout_margin
Start=
"72dp"
android:
padding
Start=
"72dp"
android:
layout_margin
End=
"24dp"
>
android:
padding
End=
"24dp"
>
<com.facebook.drawee.view.SimpleDraweeView
<com.facebook.drawee.view.SimpleDraweeView
android:id=
"@+id/image_preview"
android:id=
"@+id/image_preview"
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
7dcb3b46
...
@@ -88,4 +88,11 @@
...
@@ -88,4 +88,11 @@
<!-- Upload Messages -->
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)
</string>
<string
name=
"max_file_size_exceeded"
>
Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)
</string>
<!-- Socket status -->
<string
name=
"status_connected"
>
conectado
</string>
<string
name=
"status_disconnected"
>
desconetado
</string>
<string
name=
"status_connecting"
>
conectando
</string>
<string
name=
"status_authenticating"
>
autenticando
</string>
<string
name=
"status_disconnecting"
>
desconectando
</string>
<string
name=
"status_waiting"
>
conectando em %d segundos
</string>
</resources>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
7dcb3b46
...
@@ -90,4 +90,12 @@
...
@@ -90,4 +90,12 @@
<!-- Upload Messages -->
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
File size %1$d bytes exceeded max upload size of %2$d bytes
</string>
<string
name=
"max_file_size_exceeded"
>
File size %1$d bytes exceeded max upload size of %2$d bytes
</string>
<!-- Socket status -->
<string
name=
"status_connected"
>
connected
</string>
<string
name=
"status_disconnected"
>
disconnected
</string>
<string
name=
"status_connecting"
>
connecting
</string>
<string
name=
"status_authenticating"
>
authenticating
</string>
<string
name=
"status_disconnecting"
>
disconnecting
</string>
<string
name=
"status_waiting"
>
connecting in %d seconds
</string>
</resources>
</resources>
\ No newline at end of file
dependencies.gradle
View file @
7dcb3b46
...
@@ -11,6 +11,7 @@ ext {
...
@@ -11,6 +11,7 @@ ext {
// Main dependencies
// Main dependencies
support
:
'27.0.2'
,
support
:
'27.0.2'
,
constraintLayout
:
'1.0.2'
,
constraintLayout
:
'1.0.2'
,
androidKtx
:
'0.1'
,
dagger
:
'2.14.1'
,
dagger
:
'2.14.1'
,
exoPlayer
:
'2.6.0'
,
exoPlayer
:
'2.6.0'
,
playServices
:
'11.8.0'
,
playServices
:
'11.8.0'
,
...
@@ -49,6 +50,8 @@ ext {
...
@@ -49,6 +50,8 @@ ext {
constraintLayout
:
"com.android.support.constraint:constraint-layout:${versions.constraintLayout}"
,
constraintLayout
:
"com.android.support.constraint:constraint-layout:${versions.constraintLayout}"
,
cardView
:
"com.android.support:cardview-v7:${versions.support}"
,
cardView
:
"com.android.support:cardview-v7:${versions.support}"
,
androidKtx
:
"androidx.core:core-ktx:${versions.androidKtx}"
,
dagger
:
"com.google.dagger:dagger:${versions.dagger}"
,
dagger
:
"com.google.dagger:dagger:${versions.dagger}"
,
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment