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
5e5e565d
Unverified
Commit
5e5e565d
authored
May 18, 2018
by
Rafael Kellermann Streit
Committed by
GitHub
May 18, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1287 from RocketChat/beta
[RELEASE] Merge beta into master
parents
b0817ace
744cdeb2
Changes
81
Show whitespace changes
Inline
Side-by-side
Showing
81 changed files
with
2074 additions
and
765 deletions
+2074
-765
build.gradle
app/build.gradle
+2
-2
BaseViewHolder.kt
...va/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
+20
-15
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+67
-22
ImageAttachmentViewHolder.kt
...ket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
+35
-20
MessageReplyViewHolder.kt
...rocket/android/chatroom/adapter/MessageReplyViewHolder.kt
+30
-0
MessageViewHolder.kt
...chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
+7
-0
MessageReply.kt
.../java/chat/rocket/android/chatroom/domain/MessageReply.kt
+6
-0
ChatRoomNavigator.kt
...rocket/android/chatroom/presentation/ChatRoomNavigator.kt
+22
-2
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+243
-36
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+27
-1
MessageService.kt
...va/chat/rocket/android/chatroom/service/MessageService.kt
+14
-5
ChatRoomActivity.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
+37
-22
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+183
-53
ActionListAdapter.kt
...roid/chatroom/ui/bottomsheet/adapter/ActionListAdapter.kt
+9
-3
BaseViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
+2
-1
MessageReplyViewModel.kt
...ocket/android/chatroom/viewmodel/MessageReplyViewModel.kt
+20
-0
RoomViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/RoomViewModel.kt
+9
-0
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+63
-7
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+88
-26
ChatRoomsAdapter.kt
...java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
+7
-4
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+21
-20
ActivityBuilder.kt
...java/chat/rocket/android/dagger/module/ActivityBuilder.kt
+10
-4
AppModule.kt
.../main/java/chat/rocket/android/dagger/module/AppModule.kt
+16
-35
LocalModule.kt
...ain/java/chat/rocket/android/dagger/module/LocalModule.kt
+56
-3
ForFresco.kt
...in/java/chat/rocket/android/dagger/qualifier/ForFresco.kt
+0
-7
FavoriteMessagesFragmentModule.kt
...oid/favoritemessages/di/FavoriteMessagesFragmentModule.kt
+30
-0
FavoriteMessagesFragmentProvider.kt
...d/favoritemessages/di/FavoriteMessagesFragmentProvider.kt
+12
-0
FavoriteMessagesPresenter.kt
...avoritemessages/presentation/FavoriteMessagesPresenter.kt
+52
-0
FavoriteMessagesView.kt
...oid/favoritemessages/presentation/FavoriteMessagesView.kt
+15
-0
FavoriteMessagesFragment.kt
...t/android/favoritemessages/ui/FavoriteMessagesFragment.kt
+119
-0
FrescoAuthInterceptor.kt
.../java/chat/rocket/android/helper/FrescoAuthInterceptor.kt
+0
-30
MessageHelper.kt
...src/main/java/chat/rocket/android/helper/MessageHelper.kt
+71
-0
UserHelper.kt
app/src/main/java/chat/rocket/android/helper/UserHelper.kt
+63
-0
LocalRepository.kt
...ava/chat/rocket/android/infrastructure/LocalRepository.kt
+6
-0
SharedPreferencesLocalRepository.kt
...ndroid/infrastructure/SharedPreferencesLocalRepository.kt
+19
-1
MainNavigator.kt
...va/chat/rocket/android/main/presentation/MainNavigator.kt
+3
-3
NavHeaderViewModel.kt
.../chat/rocket/android/main/viewmodel/NavHeaderViewModel.kt
+0
-1
MembersFragment.kt
...in/java/chat/rocket/android/members/ui/MembersFragment.kt
+21
-21
PinnedMessagesPresenter.kt
...id/pinnedmessages/presentation/PinnedMessagesPresenter.kt
+4
-4
PinnedMessagesFragment.kt
...ocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt
+24
-23
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+1
-1
ChatRoomsInteractor.kt
.../chat/rocket/android/server/domain/ChatRoomsInteractor.kt
+3
-4
GetPermissionsInteractor.kt
.../rocket/android/server/domain/GetPermissionsInteractor.kt
+0
-34
PermissionsInteractor.kt
...hat/rocket/android/server/domain/PermissionsInteractor.kt
+77
-0
PermissionsRepository.kt
...hat/rocket/android/server/domain/PermissionsRepository.kt
+24
-0
RefreshSettingsInteractor.kt
...rocket/android/server/domain/RefreshSettingsInteractor.kt
+1
-1
SaveActiveUsersInteractor.kt
...rocket/android/server/domain/SaveActiveUsersInteractor.kt
+2
-1
SettingsRepository.kt
...a/chat/rocket/android/server/domain/SettingsRepository.kt
+2
-0
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+1
-1
SharedPreferencesPermissionsRepository.kt
...infraestructure/SharedPreferencesPermissionsRepository.kt
+30
-0
SharedPreferencesSettingsRepository.kt
...er/infraestructure/SharedPreferencesSettingsRepository.kt
+3
-1
Rx.kt
app/src/main/java/chat/rocket/android/util/extensions/Rx.kt
+1
-2
ic_action_message_copy_24dp.xml
app/src/main/res/drawable/ic_action_message_copy_24dp.xml
+10
-0
ic_action_message_delete_24dp.xml
app/src/main/res/drawable/ic_action_message_delete_24dp.xml
+10
-0
ic_action_message_edit_24dp.xml
app/src/main/res/drawable/ic_action_message_edit_24dp.xml
+8
-7
ic_action_message_pin_24dp.xml
app/src/main/res/drawable/ic_action_message_pin_24dp.xml
+10
-0
ic_action_message_quote_24dp.xml
app/src/main/res/drawable/ic_action_message_quote_24dp.xml
+10
-0
ic_action_message_reply_24dp.xml
app/src/main/res/drawable/ic_action_message_reply_24dp.xml
+10
-0
ic_action_message_star_24dp.xml
app/src/main/res/drawable/ic_action_message_star_24dp.xml
+10
-0
ic_arrow_back_white_24dp.xml
app/src/main/res/drawable/ic_arrow_back_white_24dp.xml
+10
-4
ic_hashtag_black.xml
app/src/main/res/drawable/ic_hashtag_black.xml
+0
-12
ic_pin_black_24dp.xml
app/src/main/res/drawable/ic_pin_black_24dp.xml
+0
-15
ic_room_channel.xml
app/src/main/res/drawable/ic_room_channel.xml
+1
-1
ic_room_dm.xml
app/src/main/res/drawable/ic_room_dm.xml
+1
-1
ic_room_lock.xml
app/src/main/res/drawable/ic_room_lock.xml
+1
-1
message_reply_button_bg.xml
app/src/main/res/drawable/message_reply_button_bg.xml
+10
-0
activity_pinned_messages.xml
app/src/main/res/layout/activity_pinned_messages.xml
+0
-19
fragment_chat_room.xml
app/src/main/res/layout/fragment_chat_room.xml
+43
-31
fragment_chat_rooms.xml
app/src/main/res/layout/fragment_chat_rooms.xml
+11
-0
fragment_favorite_messages.xml
app/src/main/res/layout/fragment_favorite_messages.xml
+77
-0
fragment_pinned_messages.xml
app/src/main/res/layout/fragment_pinned_messages.xml
+19
-23
item_message.xml
app/src/main/res/layout/item_message.xml
+47
-22
item_message_reply.xml
app/src/main/res/layout/item_message_reply.xml
+37
-0
chatroom_actions.xml
app/src/main/res/menu/chatroom_actions.xml
+5
-0
message_actions.xml
app/src/main/res/menu/message_actions.xml
+44
-48
strings.xml
app/src/main/res/values-es/strings.xml
+25
-2
strings.xml
app/src/main/res/values-fr/strings.xml
+26
-1
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+24
-0
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+29
-11
strings.xml
app/src/main/res/values/strings.xml
+18
-1
MemoryMessagesRepositoryTest.kt
.../java/chat/rocket/android/MemoryMessagesRepositoryTest.kt
+0
-150
No files found.
app/build.gradle
View file @
5e5e565d
...
...
@@ -13,8 +13,8 @@ android {
applicationId
"chat.rocket.android"
minSdkVersion
21
targetSdkVersion
versions
.
targetSdk
versionCode
202
0
versionName
"2.
1.1
"
versionCode
202
1
versionName
"2.
2.0
"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
View file @
5e5e565d
...
...
@@ -18,7 +18,6 @@ import com.google.android.flexbox.FlexboxLayoutManager
import
ru.whalemare.sheetmenu.extension.inflate
import
ru.whalemare.sheetmenu.extension.toList
abstract
class
BaseViewHolder
<
T
:
BaseViewModel
<*>>(
itemView
:
View
,
private
val
listener
:
ActionsListener
,
...
...
@@ -76,22 +75,29 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
fun
onActionSelected
(
item
:
MenuItem
,
message
:
Message
)
}
private
val
long
ClickListener
=
{
view
:
View
->
private
val
on
ClickListener
=
{
view
:
View
->
if
(
data
?.
message
?.
isSystemMessage
()
==
false
)
{
data
?.
message
?.
let
{
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
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_message_unpin
}
?.
apply
{
setTitle
(
if
(
it
.
pinned
)
R
.
string
.
action_msg_unpin
else
R
.
string
.
action_msg_pin
)
isChecked
=
it
.
pinned
}
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_message_star
}
?.
apply
{
val
isStarred
=
it
.
starred
?.
isNotEmpty
()
?:
false
setTitle
(
if
(
isStarred
)
R
.
string
.
action_msg_unstar
else
R
.
string
.
action_msg_star
)
isChecked
=
isStarred
}
val
adapter
=
ActionListAdapter
(
menuItems
,
this
@BaseViewHolder
)
BottomSheetMenu
(
adapter
).
show
(
view
.
context
)
}
true
}
}
internal
fun
setupActionMenu
(
view
:
View
)
{
if
(
listener
.
isActionsEnabled
())
{
view
.
setOnClickListener
(
onClickListener
)
if
(
view
is
ViewGroup
)
{
for
(
child
in
view
.
children
)
{
if
(
child
!
is
RecyclerView
&&
child
.
id
!=
R
.
id
.
recycler_view_reactions
)
{
...
...
@@ -99,7 +105,6 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
}
}
}
view
.
setOnLongClickListener
(
longClickListener
)
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
5e5e565d
...
...
@@ -5,7 +5,21 @@ import android.view.MenuItem
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.viewmodel.*
import
chat.rocket.android.chatroom.ui.chatRoomIntent
import
chat.rocket.android.chatroom.viewmodel.AudioAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.AuthorAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.BaseFileAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.chatroom.viewmodel.ColorAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.GenericFileAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageReplyViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.UrlPreviewViewModel
import
chat.rocket.android.chatroom.viewmodel.VideoAttachmentViewModel
import
chat.rocket.android.chatroom.viewmodel.toViewType
import
chat.rocket.android.main.presentation.MainNavigator
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.widget.emoji.EmojiReactionListener
import
chat.rocket.core.model.Message
...
...
@@ -65,6 +79,12 @@ class ChatRoomAdapter(
val
view
=
parent
.
inflate
(
R
.
layout
.
item_file_attachment
)
GenericFileAttachmentViewHolder
(
view
,
actionsListener
,
reactionListener
)
}
BaseViewModel
.
ViewType
.
MESSAGE_REPLY
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message_reply
)
MessageReplyViewHolder
(
view
,
actionsListener
,
reactionListener
)
{
roomName
,
permalink
->
presenter
?.
openDirectMessage
(
roomName
,
permalink
)
}
}
else
->
{
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
}
...
...
@@ -98,15 +118,26 @@ class ChatRoomAdapter(
}
when
(
holder
)
{
is
MessageViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageViewModel
)
is
ImageAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
ImageAttachmentViewModel
)
is
AudioAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
AudioAttachmentViewModel
)
is
VideoAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
VideoAttachmentViewModel
)
is
UrlPreviewViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
UrlPreviewViewModel
)
is
MessageAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageAttachmentViewModel
)
is
AuthorAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
AuthorAttachmentViewModel
)
is
ColorAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
ColorAttachmentViewModel
)
is
GenericFileAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
GenericFileAttachmentViewModel
)
is
MessageViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageViewModel
)
is
ImageAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
ImageAttachmentViewModel
)
is
AudioAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
AudioAttachmentViewModel
)
is
VideoAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
VideoAttachmentViewModel
)
is
UrlPreviewViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
UrlPreviewViewModel
)
is
MessageAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageAttachmentViewModel
)
is
AuthorAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
AuthorAttachmentViewModel
)
is
ColorAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
ColorAttachmentViewModel
)
is
GenericFileAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
GenericFileAttachmentViewModel
)
is
MessageReplyViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageReplyViewModel
)
}
}
...
...
@@ -181,25 +212,39 @@ class ChatRoomAdapter(
}
private
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
,
id
,
false
)
R
.
id
.
action_menu_msg_reply
->
presenter
?.
citeMessage
(
roomType
,
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
)
{
R
.
id
.
action_message_reply
->
{
presenter
?.
citeMessage
(
roomName
,
roomType
,
id
,
true
)
}
R
.
id
.
action_message_quote
->
{
presenter
?.
citeMessage
(
roomName
,
roomType
,
id
,
false
)
}
R
.
id
.
action_message_copy
->
{
presenter
?.
copyMessage
(
id
)
}
R
.
id
.
action_message_edit
->
{
presenter
?.
editMessage
(
roomId
,
id
,
message
.
message
)
}
R
.
id
.
action_message_star
->
{
if
(!
item
.
isChecked
)
{
presenter
?.
starMessage
(
id
)
}
else
{
presenter
?.
unstarMessage
(
id
)
}
}
R
.
id
.
action_message_unpin
->
{
if
(!
item
.
isChecked
)
{
presenter
?.
pinMessage
(
id
)
}
else
{
presenter
?.
unpinMessage
(
id
)
}
}
}
R
.
id
.
action_message_delete
->
presenter
?.
deleteMessage
(
roomId
,
id
)
R
.
id
.
action_menu_msg_react
->
presenter
?.
showReactions
(
id
)
else
->
TODO
(
"Not implemented"
)
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
View file @
5e5e565d
...
...
@@ -37,11 +37,11 @@ import timber.log.Timber
import
java.io.File
class
ImageAttachmentViewHolder
(
itemView
:
View
,
class
ImageAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
,
listener
,
reactionListener
)
{
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
,
listener
,
reactionListener
)
{
private
var
cacheKey
:
CacheKey
?
=
null
init
{
...
...
@@ -63,7 +63,8 @@ class ImageAttachmentViewHolder(itemView: View,
// TODO - implement a proper image viewer with a proper Transition
// TODO - We should definitely write our own ImageViewer
var
imageViewer
:
ImageViewer
?
=
null
val
request
=
ImageRequestBuilder
.
newBuilderWithSource
(
Uri
.
parse
(
data
.
attachmentUrl
))
val
request
=
ImageRequestBuilder
.
newBuilderWithSource
(
Uri
.
parse
(
data
.
attachmentUrl
))
.
setLowestPermittedRequestLevel
(
ImageRequest
.
RequestLevel
.
DISK_CACHE
)
.
build
()
...
...
@@ -71,8 +72,10 @@ class ImageAttachmentViewHolder(itemView: View,
.
getEncodedCacheKey
(
request
,
null
)
val
pad
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
viewer_toolbar_padding
)
val
lparams
=
AppBarLayout
.
LayoutParams
(
ViewGroup
.
LayoutParams
.
MATCH_PARENT
,
ViewGroup
.
LayoutParams
.
WRAP_CONTENT
)
val
lparams
=
AppBarLayout
.
LayoutParams
(
ViewGroup
.
LayoutParams
.
MATCH_PARENT
,
ViewGroup
.
LayoutParams
.
WRAP_CONTENT
)
val
toolbar
=
Toolbar
(
context
).
also
{
it
.
inflateMenu
(
R
.
menu
.
image_actions
)
it
.
overflowIcon
?.
setTint
(
Color
.
WHITE
)
...
...
@@ -98,7 +101,7 @@ class ImageAttachmentViewHolder(itemView: View,
val
backArrowView
=
ImageView
(
context
).
also
{
it
.
setImageResource
(
R
.
drawable
.
ic_arrow_back_white_24dp
)
it
.
setOnClickListener
{
imageViewer
?.
onDismiss
()
}
it
.
setPadding
(
0
,
pad
,
pad
,
pad
)
it
.
setPadding
(
0
,
pad
,
pad
,
pad
)
}
val
layoutParams
=
AppBarLayout
.
LayoutParams
(
...
...
@@ -113,10 +116,12 @@ class ImageAttachmentViewHolder(itemView: View,
val
appBarLayout
=
AppBarLayout
(
context
).
also
{
it
.
layoutParams
=
lparams
it
.
setBackgroundColor
(
Color
.
BLACK
)
it
.
addView
(
toolbar
,
AppBarLayout
.
LayoutParams
(
it
.
addView
(
toolbar
,
AppBarLayout
.
LayoutParams
(
AppBarLayout
.
LayoutParams
.
MATCH_PARENT
,
ViewGroup
.
LayoutParams
.
WRAP_CONTENT
))
)
)
}
val
builder
=
ImageViewer
.
createPipelineDraweeControllerBuilder
()
...
...
@@ -144,12 +149,17 @@ class ImageAttachmentViewHolder(itemView: View,
val
imageFormat
=
ImageFormatChecker
.
getImageFormat
(
resource
.
openStream
())
val
imageDir
=
"${Environment.DIRECTORY_PICTURES}/Rocket.Chat Images/"
val
imagePath
=
Environment
.
getExternalStoragePublicDirectory
(
imageDir
)
val
imageFile
=
File
(
imagePath
,
"${cachedFile.nameWithoutExtension}.${imageFormat.fileExtension}"
)
val
imageFile
=
File
(
imagePath
,
"${cachedFile.nameWithoutExtension}.${imageFormat.fileExtension}"
)
imagePath
.
mkdirs
()
imageFile
.
createNewFile
()
try
{
cachedFile
.
copyTo
(
imageFile
,
true
)
MediaScannerConnection
.
scanFile
(
context
,
arrayOf
(
imageFile
.
absolutePath
),
null
)
{
path
,
uri
->
MediaScannerConnection
.
scanFile
(
context
,
arrayOf
(
imageFile
.
absolutePath
),
null
)
{
path
,
uri
->
Timber
.
i
(
"Scanned $path:"
)
Timber
.
i
(
"-> uri=$uri"
)
}
...
...
@@ -166,16 +176,21 @@ class ImageAttachmentViewHolder(itemView: View,
}
private
fun
canWriteToExternalStorage
():
Boolean
{
return
AndroidPermissionsHelper
.
checkPermission
(
itemView
.
context
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
)
return
AndroidPermissionsHelper
.
checkPermission
(
itemView
.
context
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
)
}
private
fun
checkWritingPermission
()
{
val
context
=
itemView
.
context
if
(
context
is
ContextThemeWrapper
&&
context
.
baseContext
is
Activity
)
{
val
activity
=
context
.
baseContext
as
Activity
AndroidPermissionsHelper
.
requestPermission
(
activity
,
AndroidPermissionsHelper
.
requestPermission
(
activity
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
,
AndroidPermissionsHelper
.
WRITE_EXTERNAL_STORAGE_CODE
)
AndroidPermissionsHelper
.
WRITE_EXTERNAL_STORAGE_CODE
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageReplyViewHolder.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.adapter
import
android.view.View
import
chat.rocket.android.chatroom.viewmodel.MessageReplyViewModel
import
chat.rocket.android.widget.emoji.EmojiReactionListener
import
kotlinx.android.synthetic.main.item_message_reply.view.*
class
MessageReplyViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
,
private
val
replyCallback
:
(
roomName
:
String
,
permalink
:
String
)
->
Unit
)
:
BaseViewHolder
<
MessageReplyViewModel
>(
itemView
,
listener
,
reactionListener
)
{
init
{
with
(
itemView
)
{
setupActionMenu
(
itemView
)
}
}
override
fun
bindViews
(
data
:
MessageReplyViewModel
)
{
with
(
itemView
)
{
button_message_reply
.
setOnClickListener
{
with
(
data
.
rawData
)
{
replyCallback
.
invoke
(
roomName
,
permalink
)
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
View file @
5e5e565d
...
...
@@ -3,8 +3,11 @@ package chat.rocket.android.chatroom.adapter
import
android.graphics.Color
import
android.text.method.LinkMovementMethod
import
android.view.View
import
androidx.core.view.isVisible
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.widget.emoji.EmojiReactionListener
import
chat.rocket.core.model.isSystemMessage
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
...
...
@@ -33,6 +36,10 @@ class MessageViewHolder(
text_content
.
setTextColor
(
if
(
data
.
isTemporary
)
Color
.
GRAY
else
Color
.
BLACK
)
data
.
message
.
let
{
text_edit_indicator
.
isVisible
=
it
.
isSystemMessage
()
&&
it
.
editedBy
!=
null
image_star_indicator
.
isVisible
=
it
.
starred
?.
isNotEmpty
()
?:
false
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/domain/MessageReply.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.domain
data class
MessageReply
(
val
roomName
:
String
,
val
permalink
:
String
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomNavigator.kt
View file @
5e5e565d
...
...
@@ -2,6 +2,7 @@ package chat.rocket.android.chatroom.presentation
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.chatroom.ui.chatRoomIntent
import
chat.rocket.android.members.ui.newInstance
import
chat.rocket.android.server.ui.changeServerIntent
import
chat.rocket.android.util.extensions.addFragmentBackStack
...
...
@@ -15,8 +16,14 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
}
fun
toPinnedMessageList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
{
activity
.
addFragmentBackStack
(
"PinnedMessages"
,
R
.
id
.
fragment_container
){
chat
.
rocket
.
android
.
pinnedmessages
.
ui
.
newInstance
(
chatRoomId
,
chatRoomType
)
activity
.
addFragmentBackStack
(
"PinnedMessages"
,
R
.
id
.
fragment_container
)
{
chat
.
rocket
.
android
.
pinnedmessages
.
ui
.
newInstance
(
chatRoomId
,
chatRoomType
)
}
}
fun
toFavoriteMessageList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
{
activity
.
addFragmentBackStack
(
"FavoriteMessages"
,
R
.
id
.
fragment_container
)
{
chat
.
rocket
.
android
.
favoritemessages
.
ui
.
newInstance
(
chatRoomId
,
chatRoomType
)
}
}
...
...
@@ -24,4 +31,17 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
activity
.
startActivity
(
activity
.
changeServerIntent
())
activity
.
finish
()
}
fun
toDirectMessage
(
chatRoomId
:
String
,
chatRoomName
:
String
,
chatRoomType
:
String
,
isChatRoomReadOnly
:
Boolean
,
chatRoomLastSeen
:
Long
,
isChatRoomSubscribed
:
Boolean
,
isChatRoomCreator
:
Boolean
,
chatRoomMessage
:
String
)
{
activity
.
startActivity
(
activity
.
chatRoomIntent
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
isChatRoomReadOnly
,
chatRoomLastSeen
,
isChatRoomSubscribed
,
isChatRoomCreator
,
chatRoomMessage
))
activity
.
overridePendingTransition
(
R
.
anim
.
open_enter
,
R
.
anim
.
open_exit
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
5e5e565d
...
...
@@ -6,15 +6,28 @@ import chat.rocket.android.chatroom.adapter.AutoCompleteType
import
chat.rocket.android.chatroom.adapter.PEOPLE
import
chat.rocket.android.chatroom.adapter.ROOMS
import
chat.rocket.android.chatroom.domain.UriInteractor
import
chat.rocket.android.chatroom.viewmodel.RoomViewModel
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import
chat.rocket.android.core.behaviours.showMessage
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.MessageHelper
import
chat.rocket.android.helper.UserHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.username
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.ChatRoomsInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.JobSchedulerInteractor
import
chat.rocket.android.server.domain.MessagesRepository
import
chat.rocket.android.server.domain.PermissionsInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.RoomRepository
import
chat.rocket.android.server.domain.UsersRepository
import
chat.rocket.android.server.domain.uploadMaxFileSize
import
chat.rocket.android.server.domain.uploadMimeTypeFilter
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extensions.avatarUrl
...
...
@@ -26,8 +39,30 @@ import chat.rocket.common.model.SimpleUser
import
chat.rocket.common.model.UserStatus
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.realtime.setTypingStatus
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.internal.realtime.subscribeTypingStatus
import
chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.chatRoomRoles
import
chat.rocket.core.internal.rest.commands
import
chat.rocket.core.internal.rest.deleteMessage
import
chat.rocket.core.internal.rest.getMembers
import
chat.rocket.core.internal.rest.history
import
chat.rocket.core.internal.rest.joinChat
import
chat.rocket.core.internal.rest.markAsRead
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.messages
import
chat.rocket.core.internal.rest.pinMessage
import
chat.rocket.core.internal.rest.runCommand
import
chat.rocket.core.internal.rest.sendMessage
import
chat.rocket.core.internal.rest.spotlight
import
chat.rocket.core.internal.rest.starMessage
import
chat.rocket.core.internal.rest.toggleReaction
import
chat.rocket.core.internal.rest.unpinMessage
import
chat.rocket.core.internal.rest.unstarMessage
import
chat.rocket.core.internal.rest.updateMessage
import
chat.rocket.core.internal.rest.uploadFile
import
chat.rocket.core.model.ChatRoomRole
import
chat.rocket.core.model.Command
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Myself
...
...
@@ -45,30 +80,69 @@ class ChatRoomPresenter @Inject constructor(
private
val
view
:
ChatRoomView
,
private
val
navigator
:
ChatRoomNavigator
,
private
val
strategy
:
CancelStrategy
,
getSettingsInteractor
:
GetSettingsInteractor
,
serverInteractor
:
GetCurrentServerInteractor
,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
permissions
:
GetPermissionsInteractor
,
private
val
chatRoomsInteractor
:
ChatRoomsInteractor
,
private
val
permissions
:
PermissionsInteractor
,
private
val
uriInteractor
:
UriInteractor
,
private
val
messagesRepository
:
MessagesRepository
,
private
val
usersRepository
:
UsersRepository
,
private
val
roomsRepository
:
RoomRepository
,
private
val
localRepository
:
LocalRepository
,
factory
:
ConnectionManagerFactory
,
private
val
userHelper
:
UserHelper
,
private
val
mapper
:
ViewModelMapper
,
private
val
jobSchedulerInteractor
:
JobSchedulerInteractor
private
val
jobSchedulerInteractor
:
JobSchedulerInteractor
,
private
val
messageHelper
:
MessageHelper
,
getSettingsInteractor
:
GetSettingsInteractor
,
serverInteractor
:
GetCurrentServerInteractor
,
factory
:
ConnectionManagerFactory
)
{
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
manager
=
factory
.
create
(
currentServer
)
private
val
client
=
manager
.
client
private
var
settings
:
PublicSettings
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
private
val
currentLoggedUsername
=
userHelper
.
username
()
private
val
messagesChannel
=
Channel
<
Message
>()
private
var
chatRoomId
:
String
?
=
null
private
var
chatRoomType
:
String
?
=
null
private
var
chatIsBroadcast
:
Boolean
=
false
private
var
chatRoles
=
emptyList
<
ChatRoomRole
>()
private
val
stateChannel
=
Channel
<
State
>()
private
var
typingStatusSubscriptionId
:
String
?
=
null
private
var
lastState
=
manager
.
state
private
var
typingStatusList
=
arrayListOf
<
String
>()
fun
setupChatRoom
(
roomId
:
String
,
roomName
:
String
,
roomType
:
String
,
chatRoomMessage
:
String
?
=
null
)
{
launchUI
(
strategy
)
{
try
{
chatRoles
=
if
(
roomTypeOf
(
roomType
)
!
is
RoomType
.
DirectMessage
)
{
client
.
chatRoomRoles
(
roomType
=
roomTypeOf
(
roomType
),
roomName
=
roomName
)
}
else
emptyList
()
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
chatRoles
=
emptyList
()
}
finally
{
// User has at least an 'owner' or 'moderator' role.
val
userCanMod
=
isOwnerOrMod
()
// Can post anyway if has the 'post-readonly' permission on server.
val
userCanPost
=
userCanMod
||
permissions
.
canPostToReadOnlyChannels
()
chatIsBroadcast
=
chatRoomsInteractor
.
getById
(
currentServer
,
roomId
)
?.
run
{
broadcast
}
?:
false
view
.
onRoomUpdated
(
userCanPost
,
chatIsBroadcast
,
userCanMod
)
loadMessages
(
roomId
,
roomType
)
chatRoomMessage
?.
let
{
messageHelper
.
messageIdFromPermalink
(
it
)
}
?.
let
{
messageId
->
val
name
=
messageHelper
.
roomNameFromPermalink
(
chatRoomMessage
)
citeMessage
(
name
!!
,
messageHelper
.
roomTypeFromPermalink
(
chatRoomMessage
)
!!
,
messageId
,
true
)
}
}
}
}
private
fun
isOwnerOrMod
():
Boolean
{
return
chatRoles
.
firstOrNull
{
it
.
user
.
username
==
currentLoggedUsername
}
?.
roles
?.
any
{
it
==
"owner"
||
it
==
"moderator"
}
?:
false
}
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
this
.
chatRoomId
=
chatRoomId
...
...
@@ -78,7 +152,8 @@ class ChatRoomPresenter @Inject constructor(
try
{
if
(
offset
==
0L
)
{
val
localMessages
=
messagesRepository
.
getByRoomId
(
chatRoomId
)
val
oldMessages
=
mapper
.
map
(
localMessages
)
val
oldMessages
=
mapper
.
map
(
localMessages
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
))
if
(
oldMessages
.
isNotEmpty
())
{
view
.
showMessages
(
oldMessages
)
loadMissingMessages
()
...
...
@@ -105,6 +180,7 @@ class ChatRoomPresenter @Inject constructor(
view
.
hideLoading
()
}
subscribeTypingStatus
()
if
(
offset
==
0L
)
{
subscribeState
()
}
...
...
@@ -117,8 +193,8 @@ class ChatRoomPresenter @Inject constructor(
client
.
messages
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
30
).
result
}
messagesRepository
.
saveAll
(
messages
)
v
al
allMessages
=
mapper
.
map
(
messages
)
view
.
showMessages
(
allMessages
)
v
iew
.
showMessages
(
mapper
.
map
(
messages
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
))
)
}
fun
sendMessage
(
chatRoomId
:
String
,
text
:
String
,
messageId
:
String
?)
{
...
...
@@ -127,7 +203,7 @@ class ChatRoomPresenter @Inject constructor(
// ignore message for now, will receive it on the stream
val
id
=
UUID
.
randomUUID
().
toString
()
val
message
=
if
(
messageId
==
null
)
{
val
username
=
localRepository
.
username
()
val
username
=
userHelper
.
username
()
val
newMessage
=
Message
(
id
=
id
,
roomId
=
chatRoomId
,
...
...
@@ -142,6 +218,7 @@ class ChatRoomPresenter @Inject constructor(
groupable
=
false
,
parseUrls
=
false
,
pinned
=
false
,
starred
=
emptyList
(),
mentions
=
emptyList
(),
reactions
=
null
,
senderAlias
=
null
,
...
...
@@ -151,9 +228,10 @@ class ChatRoomPresenter @Inject constructor(
isTemporary
=
true
)
try
{
val
message
=
client
.
sendMessage
(
id
,
chatRoomId
,
text
)
messagesRepository
.
save
(
newMessage
)
view
.
showNewMessage
(
mapper
.
map
(
newMessage
))
val
message
=
client
.
sendMessage
(
id
,
chatRoomId
,
text
)
view
.
showNewMessage
(
mapper
.
map
(
newMessage
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
)))
message
}
catch
(
ex
:
Exception
)
{
// Ok, not very beautiful, but the backend sends us a not valid response
...
...
@@ -219,6 +297,22 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun
sendTyping
()
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
currentLoggedUsername
!=
null
)
{
client
.
setTypingStatus
(
chatRoomId
.
toString
(),
currentLoggedUsername
,
true
)
}
}
}
fun
sendNotTyping
()
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
currentLoggedUsername
!=
null
)
{
client
.
setTypingStatus
(
chatRoomId
.
toString
(),
currentLoggedUsername
,
false
)
}
}
}
private
fun
markRoomAsRead
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
try
{
...
...
@@ -278,7 +372,8 @@ class ChatRoomPresenter @Inject constructor(
Timber
.
d
(
"History: $messages"
)
if
(
messages
.
result
.
isNotEmpty
())
{
val
models
=
mapper
.
map
(
messages
.
result
)
val
models
=
mapper
.
map
(
messages
.
result
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
))
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
...
...
@@ -300,14 +395,6 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun
unsubscribeMessages
(
chatRoomId
:
String
)
{
manager
.
removeStatusChannel
(
stateChannel
)
manager
.
unsubscribeRoomMessages
(
chatRoomId
)
// All messages during the subscribed period are assumed to be read,
// and lastSeen is updated as the time when the user leaves the room
markRoomAsRead
(
chatRoomId
)
}
/**
* Delete the message with the given id.
*
...
...
@@ -343,7 +430,7 @@ class ChatRoomPresenter @Inject constructor(
* @param messageId The id of the message to make citation for.
* @param mentionAuthor true means the citation is a reply otherwise it's a quote.
*/
fun
citeMessage
(
roomType
:
String
,
messageId
:
String
,
mentionAuthor
:
Boolean
)
{
fun
citeMessage
(
room
Name
:
String
,
room
Type
:
String
,
messageId
:
String
,
mentionAuthor
:
Boolean
)
{
launchUI
(
strategy
)
{
val
message
=
messagesRepository
.
getById
(
messageId
)
val
me
:
Myself
?
=
try
{
...
...
@@ -356,11 +443,19 @@ class ChatRoomPresenter @Inject constructor(
val
id
=
msg
.
id
val
username
=
msg
.
sender
?.
username
?:
""
val
mention
=
if
(
mentionAuthor
&&
me
?.
username
!=
username
)
"@$username"
else
""
val
room
=
if
(
roomTypeOf
(
roomType
)
is
RoomType
.
DirectMessage
)
username
else
roomType
val
room
=
if
(
roomTypeOf
(
roomType
)
is
RoomType
.
DirectMessage
)
username
else
roomName
val
chatRoomType
=
when
(
roomTypeOf
(
roomType
))
{
is
RoomType
.
DirectMessage
->
"direct"
is
RoomType
.
PrivateGroup
->
"group"
is
RoomType
.
Channel
->
"channel"
is
RoomType
.
Livechat
->
"livechat"
else
->
"custom"
}
view
.
showReplyingAction
(
username
=
getDisplayName
(
msg
.
sender
),
replyMarkdown
=
"[ ]($currentServer/$roomType/$room?msg=$id) $mention "
,
quotedMessage
=
mapper
.
map
(
message
).
last
().
preview
?.
message
?:
""
replyMarkdown
=
"[ ]($currentServer/$chatRoomType/$room?msg=$id) $mention "
,
quotedMessage
=
mapper
.
map
(
message
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
)).
last
().
preview
?.
message
?:
""
)
}
}
...
...
@@ -405,6 +500,34 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun
starMessage
(
messageId
:
String
)
{
launchUI
(
strategy
)
{
if
(!
permissions
.
allowedMessageStarring
())
{
view
.
showMessage
(
R
.
string
.
permission_starring_not_allowed
)
return
@launchUI
}
try
{
retryIO
(
"starMessage($messageId)"
)
{
client
.
starMessage
(
messageId
)
}
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
}
}
}
fun
unstarMessage
(
messageId
:
String
)
{
launchUI
(
strategy
)
{
if
(!
permissions
.
allowedMessageStarring
())
{
view
.
showMessage
(
R
.
string
.
permission_starring_not_allowed
)
return
@launchUI
}
try
{
retryIO
(
"unstarMessage($messageId)"
)
{
client
.
unstarMessage
(
messageId
)
}
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
}
}
}
fun
pinMessage
(
messageId
:
String
)
{
launchUI
(
strategy
)
{
if
(!
permissions
.
allowedMessagePinning
())
{
...
...
@@ -515,14 +638,19 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toMembersList
(
chatRoomId
,
chatRoomType
)
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toMembersList
(
chatRoomId
,
chatRoomType
)
fun
toPinnedMessageList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toPinnedMessageList
(
chatRoomId
,
chatRoomType
)
fun
toPinnedMessageList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toPinnedMessageList
(
chatRoomId
,
chatRoomType
)
fun
toFavoriteMessageList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toFavoriteMessageList
(
chatRoomId
,
chatRoomType
)
fun
loadChatRooms
()
{
launchUI
(
strategy
)
{
try
{
val
chatRooms
=
getC
hatRoomsInteractor
.
getAll
(
currentServer
)
val
chatRooms
=
c
hatRoomsInteractor
.
getAll
(
currentServer
)
.
filterNot
{
it
.
type
is
RoomType
.
DirectMessage
||
it
.
type
is
RoomType
.
Livechat
}
...
...
@@ -547,20 +675,47 @@ class ChatRoomPresenter @Inject constructor(
launchUI
(
strategy
)
{
try
{
retryIO
(
"joinChat($chatRoomId)"
)
{
client
.
joinChat
(
chatRoomId
)
}
view
.
onJoined
()
val
canPost
=
permissions
.
canPostToReadOnlyChannels
()
view
.
onJoined
(
canPost
)
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
}
}
}
fun
openDirectMessage
(
roomName
:
String
,
message
:
String
)
{
launchUI
(
strategy
)
{
try
{
chatRoomsInteractor
.
getByName
(
currentServer
,
roomName
)
?.
let
{
if
(
it
.
type
is
RoomType
.
DirectMessage
)
{
navigator
.
toDirectMessage
(
chatRoomId
=
it
.
id
,
chatRoomType
=
it
.
type
.
toString
(),
chatRoomLastSeen
=
it
.
lastSeen
?:
-
1
,
chatRoomName
=
roomName
,
isChatRoomCreator
=
false
,
isChatRoomReadOnly
=
false
,
isChatRoomSubscribed
=
it
.
open
,
chatRoomMessage
=
message
)
}
else
{
throw
IllegalStateException
(
"Not a direct-message"
)
}
}
}
catch
(
ex
:
Exception
)
{
Timber
.
e
(
ex
)
view
.
showMessage
(
ex
.
message
!!
)
}
}
}
/**
* Send an emoji reaction to a message.
*/
fun
react
(
messageId
:
String
,
emoji
:
String
)
{
launchUI
(
strategy
)
{
try
{
retryIO
(
"to
o
gleEmoji($messageId, $emoji)"
)
{
retryIO
(
"to
g
gleEmoji($messageId, $emoji)"
)
{
client
.
toggleReaction
(
messageId
,
emoji
.
removeSurrounding
(
":"
))
}
}
catch
(
ex
:
RocketChatException
)
{
...
...
@@ -624,9 +779,61 @@ class ChatRoomPresenter @Inject constructor(
}
}
fun
disconnect
()
{
unsubscribeTypingStatus
()
if
(
chatRoomId
!=
null
)
{
unsubscribeMessages
(
chatRoomId
.
toString
())
}
}
private
suspend
fun
subscribeTypingStatus
()
{
client
.
subscribeTypingStatus
(
chatRoomId
.
toString
())
{
_
,
id
->
typingStatusSubscriptionId
=
id
}
for
(
typingStatus
in
client
.
typingStatusChannel
)
{
processTypingStatus
(
typingStatus
)
}
}
private
fun
processTypingStatus
(
typingStatus
:
Pair
<
String
,
Boolean
>)
{
if
(!
typingStatusList
.
any
{
username
->
username
==
typingStatus
.
first
})
{
if
(
typingStatus
.
second
)
{
typingStatusList
.
add
(
typingStatus
.
first
)
}
}
else
{
typingStatusList
.
find
{
username
->
username
==
typingStatus
.
first
}
?.
let
{
typingStatusList
.
remove
(
it
)
if
(
typingStatus
.
second
)
{
typingStatusList
.
add
(
typingStatus
.
first
)
}
}
}
if
(
typingStatusList
.
isNotEmpty
())
{
view
.
showTypingStatus
(
typingStatusList
)
}
else
{
view
.
hideTypingStatusView
()
}
}
private
fun
unsubscribeTypingStatus
()
{
typingStatusSubscriptionId
?.
let
{
client
.
unsubscribe
(
it
)
}
}
private
fun
unsubscribeMessages
(
chatRoomId
:
String
)
{
manager
.
removeStatusChannel
(
stateChannel
)
manager
.
unsubscribeRoomMessages
(
chatRoomId
)
// All messages during the subscribed period are assumed to be read,
// and lastSeen is updated as the time when the user leaves the room
markRoomAsRead
(
chatRoomId
)
}
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
launchUI
(
strategy
)
{
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
)
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
))
val
roomMessages
=
messagesRepository
.
getByRoomId
(
streamedMessage
.
roomId
)
val
index
=
roomMessages
.
indexOfFirst
{
msg
->
msg
.
id
==
streamedMessage
.
id
}
if
(
index
>
-
1
)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
5e5e565d
...
...
@@ -8,6 +8,7 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewMod
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.model.ChatRoom
interface
ChatRoomView
:
LoadingView
,
MessageView
{
...
...
@@ -25,6 +26,18 @@ interface ChatRoomView : LoadingView, MessageView {
*/
fun
sendMessage
(
text
:
String
)
/**
* Shows the username(s) of the user(s) who is/are typing in the chat room.
*
* @param usernameList The list of username to show.
*/
fun
showTypingStatus
(
usernameList
:
ArrayList
<
String
>)
/**
* Hides the typing status view.
*/
fun
hideTypingStatusView
()
/**
* Perform file selection with the mime type [filter]
*/
...
...
@@ -109,8 +122,10 @@ interface ChatRoomView : LoadingView, MessageView {
fun
populateRoomSuggestions
(
chatRooms
:
List
<
ChatRoomSuggestionViewModel
>)
/**
* This user has joined the chat callback.
*
* @param userCanPost Whether the user can post a message or not.
*/
fun
onJoined
()
fun
onJoined
(
userCanPost
:
Boolean
)
fun
showReactionsPopup
(
messageId
:
String
)
...
...
@@ -120,4 +135,15 @@ interface ChatRoomView : LoadingView, MessageView {
* @param commands The list of available commands.
*/
fun
populateCommandSuggestions
(
commands
:
List
<
CommandSuggestionViewModel
>)
/**
* Communicate whether it's a broadcast channel and if current user can post to it.
*/
fun
onRoomUpdated
(
userCanPost
:
Boolean
,
channelIsBroadcast
:
Boolean
,
userCanMod
:
Boolean
)
/**
* Open a DM with the user in the given [chatRoom] and pass the [permalink] for the message
* to reply.
*/
fun
openDirectMessage
(
chatRoom
:
ChatRoom
,
permalink
:
String
)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt
View file @
5e5e565d
...
...
@@ -60,8 +60,16 @@ class MessageService : JobService() {
)
messageRepository
.
save
(
message
.
copy
(
isTemporary
=
false
))
Timber
.
d
(
"Sent scheduled message given by id: ${message.id}"
)
}
catch
(
ex
:
RocketChat
Exception
)
{
}
catch
(
ex
:
Exception
)
{
Timber
.
e
(
ex
)
// TODO - remove the generic message when we implement :userId:/message subscription
if
(
ex
is
IllegalStateException
)
{
Timber
.
d
(
ex
,
"Probably a read-only problem..."
)
// TODO: For now we are only going to reschedule when api is fixed.
messageRepository
.
removeById
(
message
.
id
)
jobFinished
(
params
,
false
)
}
else
{
// some other error
if
(
ex
.
message
?.
contains
(
"E11000"
,
true
)
==
true
)
{
// XXX: Temporary solution. We need proper error codes from the api.
messageRepository
.
save
(
message
.
copy
(
isTemporary
=
false
))
...
...
@@ -71,6 +79,7 @@ class MessageService : JobService() {
}
}
}
}
companion
object
{
const
val
RETRY_SEND_MESSAGE_ID
=
1
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
View file @
5e5e565d
package
chat.rocket.android.chatroom.ui
import
DrawableHelper
import
android.content.Context
import
android.content.Intent
import
android.os.Bundle
...
...
@@ -27,38 +26,49 @@ fun Context.chatRoomIntent(
chatRoomType
:
String
,
isChatRoomReadOnly
:
Boolean
,
chatRoomLastSeen
:
Long
,
isChatRoomSubscribed
:
Boolean
=
true
isChatRoomSubscribed
:
Boolean
=
true
,
isChatRoomCreator
:
Boolean
=
false
,
chatRoomMessage
:
String
?
=
null
):
Intent
{
return
Intent
(
this
,
ChatRoomActivity
::
class
.
java
).
apply
{
putExtra
(
INTENT_CHAT_ROOM_ID
,
chatRoomId
)
putExtra
(
INTENT_CHAT_ROOM_NAME
,
chatRoomName
)
putExtra
(
INTENT_CHAT_ROOM_TYPE
,
chatRoomType
)
putExtra
(
INTENT_
IS_CHAT_ROOM
_READ_ONLY
,
isChatRoomReadOnly
)
putExtra
(
INTENT_
CHAT_ROOM_IS
_READ_ONLY
,
isChatRoomReadOnly
)
putExtra
(
INTENT_CHAT_ROOM_LAST_SEEN
,
chatRoomLastSeen
)
putExtra
(
INTENT_CHAT_IS_SUBSCRIBED
,
isChatRoomSubscribed
)
putExtra
(
INTENT_CHAT_ROOM_IS_CREATOR
,
isChatRoomCreator
)
putExtra
(
INTENT_CHAT_ROOM_MESSAGE
,
chatRoomMessage
)
}
}
private
const
val
INTENT_CHAT_ROOM_ID
=
"chat_room_id"
private
const
val
INTENT_CHAT_ROOM_NAME
=
"chat_room_name"
private
const
val
INTENT_CHAT_ROOM_TYPE
=
"chat_room_type"
private
const
val
INTENT_IS_CHAT_ROOM_READ_ONLY
=
"is_chat_room_read_only"
private
const
val
INTENT_CHAT_ROOM_IS_READ_ONLY
=
"chat_room_is_read_only"
private
const
val
INTENT_CHAT_ROOM_IS_CREATOR
=
"chat_room_is_creator"
private
const
val
INTENT_CHAT_ROOM_LAST_SEEN
=
"chat_room_last_seen"
private
const
val
INTENT_CHAT_IS_SUBSCRIBED
=
"is_chat_room_subscribed"
private
const
val
INTENT_CHAT_ROOM_MESSAGE
=
"chat_room_message"
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
navigator
:
ChatRoomNavigator
@Inject
lateinit
var
managerFactory
:
ConnectionManagerFactory
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
navigator
:
ChatRoomNavigator
@Inject
lateinit
var
managerFactory
:
ConnectionManagerFactory
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomType
:
String
private
var
isChatRoomReadOnly
:
Boolean
=
false
private
var
isChatRoomSubscribed
:
Boolean
=
true
private
var
isChatRoomCreator
:
Boolean
=
false
private
var
chatRoomLastSeen
:
Long
=
-
1L
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
...
...
@@ -84,8 +94,13 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
chatRoomType
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_TYPE
)
requireNotNull
(
chatRoomType
)
{
"no chat_room_type provided in Intent extras"
}
isChatRoomReadOnly
=
intent
.
getBooleanExtra
(
INTENT_IS_CHAT_ROOM_READ_ONLY
,
true
)
requireNotNull
(
chatRoomType
)
{
"no is_chat_room_read_only provided in Intent extras"
}
isChatRoomReadOnly
=
intent
.
getBooleanExtra
(
INTENT_CHAT_ROOM_IS_READ_ONLY
,
true
)
requireNotNull
(
isChatRoomReadOnly
)
{
"no chat_room_is_read_only provided in Intent extras"
}
isChatRoomCreator
=
intent
.
getBooleanExtra
(
INTENT_CHAT_ROOM_IS_CREATOR
,
false
)
requireNotNull
(
isChatRoomCreator
)
{
"no chat_room_is_creator provided in Intent extras"
}
val
chatRoomMessage
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_MESSAGE
)
setupToolbar
()
...
...
@@ -96,7 +111,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
if
(
supportFragmentManager
.
findFragmentByTag
(
TAG_CHAT_ROOM_FRAGMENT
)
==
null
)
{
addFragment
(
TAG_CHAT_ROOM_FRAGMENT
,
R
.
id
.
fragment_container
)
{
newInstance
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
isChatRoomReadOnly
,
chatRoomLastSeen
,
isChatRoomSubscribed
)
isChatRoomSubscribed
,
isChatRoomCreator
,
chatRoomMessage
)
}
}
}
...
...
@@ -109,6 +124,17 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
return
fragmentDispatchingAndroidInjector
}
private
fun
setupToolbar
()
{
setSupportActionBar
(
toolbar
)
supportActionBar
?.
setDisplayShowTitleEnabled
(
false
)
toolbar
.
setNavigationIcon
(
R
.
drawable
.
ic_arrow_back_white_24dp
)
text_room_name
.
textContent
=
chatRoomName
showRoomTypeIcon
(
true
)
toolbar
.
setNavigationOnClickListener
{
finishActivity
()
}
}
fun
showRoomTypeIcon
(
showRoomTypeIcon
:
Boolean
)
{
if
(
showRoomTypeIcon
)
{
val
roomType
=
roomTypeOf
(
chatRoomType
)
...
...
@@ -136,17 +162,6 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
}
}
private
fun
setupToolbar
()
{
setSupportActionBar
(
toolbar
)
supportActionBar
?.
setDisplayShowTitleEnabled
(
false
)
text_room_name
.
textContent
=
chatRoomName
showRoomTypeIcon
(
true
)
toolbar
.
setNavigationOnClickListener
{
finishActivity
()
}
}
fun
setupToolbarTitle
(
toolbarTitle
:
String
)
{
text_room_name
.
textContent
=
toolbarTitle
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
5e5e565d
...
...
@@ -13,9 +13,22 @@ import android.support.v4.app.Fragment
import
android.support.v7.widget.DefaultItemAnimator
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.*
import
android.view.KeyEvent
import
android.view.LayoutInflater
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.view.View
import
android.view.ViewGroup
import
android.text.SpannableStringBuilder
import
androidx.core.text.bold
import
androidx.core.view.isVisible
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.*
import
chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import
chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import
chat.rocket.android.chatroom.adapter.PEOPLE
import
chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import
chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.presentation.ChatRoomView
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
...
...
@@ -26,15 +39,37 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewMod
import
chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.widget.emoji.*
import
chat.rocket.android.util.extensions.asObservable
import
chat.rocket.android.util.extensions.circularRevealOrUnreveal
import
chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.hideKeyboard
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.isAtBottom
import
chat.rocket.android.util.extensions.rotateBy
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.android.util.extensions.ui
import
chat.rocket.android.widget.emoji.ComposerEditText
import
chat.rocket.android.widget.emoji.Emoji
import
chat.rocket.android.widget.emoji.EmojiKeyboardListener
import
chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import
chat.rocket.android.widget.emoji.EmojiListenerAdapter
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiPickerPopup
import
chat.rocket.android.widget.emoji.EmojiReactionListener
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.model.ChatRoom
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.Observable
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.Disposable
import
kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.message_attachment_options.*
import
kotlinx.android.synthetic.main.message_composer.*
import
kotlinx.android.synthetic.main.message_list.*
import
java.util.concurrent.TimeUnit
import
java.util.concurrent.atomic.AtomicInteger
import
javax.inject.Inject
...
...
@@ -44,7 +79,9 @@ fun newInstance(
chatRoomType
:
String
,
isChatRoomReadOnly
:
Boolean
,
chatRoomLastSeen
:
Long
,
isSubscribed
:
Boolean
=
true
isSubscribed
:
Boolean
=
true
,
isChatRoomCreator
:
Boolean
=
false
,
chatRoomMessage
:
String
?
=
null
):
Fragment
{
return
ChatRoomFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
...
...
@@ -54,6 +91,8 @@ fun newInstance(
putBoolean
(
BUNDLE_IS_CHAT_ROOM_READ_ONLY
,
isChatRoomReadOnly
)
putLong
(
BUNDLE_CHAT_ROOM_LAST_SEEN
,
chatRoomLastSeen
)
putBoolean
(
BUNDLE_CHAT_ROOM_IS_SUBSCRIBED
,
isSubscribed
)
putBoolean
(
BUNDLE_CHAT_ROOM_IS_CREATOR
,
isChatRoomCreator
)
putString
(
BUNDLE_CHAT_ROOM_MESSAGE
,
chatRoomMessage
)
}
}
}
...
...
@@ -65,8 +104,11 @@ private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private
const
val
REQUEST_CODE_FOR_PERFORM_SAF
=
42
private
const
val
BUNDLE_CHAT_ROOM_LAST_SEEN
=
"chat_room_last_seen"
private
const
val
BUNDLE_CHAT_ROOM_IS_SUBSCRIBED
=
"chat_room_is_subscribed"
private
const
val
BUNDLE_CHAT_ROOM_IS_CREATOR
=
"chat_room_is_creator"
private
const
val
BUNDLE_CHAT_ROOM_MESSAGE
=
"chat_room_message"
class
ChatRoomFragment
:
Fragment
(),
ChatRoomView
,
EmojiKeyboardListener
,
EmojiReactionListener
{
@Inject
lateinit
var
presenter
:
ChatRoomPresenter
@Inject
...
...
@@ -75,8 +117,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomType
:
String
private
var
chatRoomMessage
:
String
?
=
null
private
var
isSubscribed
:
Boolean
=
true
private
var
isChatRoomReadOnly
:
Boolean
=
false
private
var
isChatRoomCreator
:
Boolean
=
false
private
var
isBroadcastChannel
:
Boolean
=
false
private
lateinit
var
emojiKeyboardPopup
:
EmojiKeyboardPopup
private
var
chatRoomLastSeen
:
Long
=
-
1
private
lateinit
var
actionSnackbar
:
ActionSnackbar
...
...
@@ -87,8 +132,18 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
var
playComposeMessageButtonsAnimation
=
true
// For reveal and unreveal anim.
private
val
hypotenuse
by
lazy
{
Math
.
hypot
(
root_layout
.
width
.
toDouble
(),
root_layout
.
height
.
toDouble
()).
toFloat
()
}
private
val
max
by
lazy
{
Math
.
max
(
layout_message_attachment_options
.
width
.
toDouble
(),
layout_message_attachment_options
.
height
.
toDouble
()).
toFloat
()
}
private
val
hypotenuse
by
lazy
{
Math
.
hypot
(
root_layout
.
width
.
toDouble
(),
root_layout
.
height
.
toDouble
()
).
toFloat
()
}
private
val
max
by
lazy
{
Math
.
max
(
layout_message_attachment_options
.
width
.
toDouble
(),
layout_message_attachment_options
.
height
.
toDouble
()
).
toFloat
()
}
private
val
centerX
by
lazy
{
recycler_view
.
right
}
private
val
centerY
by
lazy
{
recycler_view
.
bottom
}
private
val
handler
=
Handler
()
...
...
@@ -97,6 +152,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
setHasOptionsMenu
(
true
)
val
bundle
=
arguments
if
(
bundle
!=
null
)
{
...
...
@@ -106,10 +162,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
isChatRoomReadOnly
=
bundle
.
getBoolean
(
BUNDLE_IS_CHAT_ROOM_READ_ONLY
)
isSubscribed
=
bundle
.
getBoolean
(
BUNDLE_CHAT_ROOM_IS_SUBSCRIBED
)
chatRoomLastSeen
=
bundle
.
getLong
(
BUNDLE_CHAT_ROOM_LAST_SEEN
)
isChatRoomCreator
=
bundle
.
getBoolean
(
BUNDLE_CHAT_ROOM_IS_CREATOR
)
chatRoomMessage
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_MESSAGE
)
}
else
{
requireNotNull
(
bundle
)
{
"no arguments supplied when the fragment was instantiated"
}
}
setHasOptionsMenu
(
true
)
}
override
fun
onCreateView
(
...
...
@@ -124,11 +181,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
super
.
onViewCreated
(
view
,
savedInstanceState
)
setupToolbar
(
chatRoomName
)
presenter
.
loadMessages
(
chatRoomId
,
chatRoomTyp
e
)
presenter
.
setupChatRoom
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
chatRoomMessag
e
)
presenter
.
loadChatRooms
()
setupRecyclerView
()
setupFab
()
setupMessageComposer
()
setupSuggestionsView
()
setupActionSnackbar
()
activity
?.
apply
{
...
...
@@ -146,9 +202,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
recycler_view
.
removeOnScrollListener
(
onScrollListener
)
recycler_view
.
removeOnLayoutChangeListener
(
layoutChangeListener
)
presenter
.
unsubscribeMessages
(
chatRoomId
)
presenter
.
disconnect
(
)
handler
.
removeCallbacksAndMessages
(
null
)
unsubscribeTextMessage
()
unsubscribe
Compose
TextMessage
()
// Hides the keyboard (if it's opened) before going to any view.
activity
?.
apply
{
...
...
@@ -168,6 +224,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
inflater
.
inflate
(
R
.
menu
.
chatroom_actions
,
menu
)
menu
.
findItem
(
R
.
id
.
action_members_list
)
?.
isVisible
=
!
isBroadcastChannel
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
...
...
@@ -176,7 +233,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter
.
toMembersList
(
chatRoomId
,
chatRoomType
)
}
R
.
id
.
action_pinned_messages
->
{
presenter
.
toPinnedMessageList
(
chatRoomId
,
chatRoomType
)
presenter
.
toPinnedMessageList
(
chatRoomId
,
chatRoomType
)
}
R
.
id
.
action_favorite_messages
->
{
presenter
.
toFavoriteMessageList
(
chatRoomId
,
chatRoomType
)
}
}
return
true
...
...
@@ -206,8 +266,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
if
(
recycler_view
.
adapter
==
null
)
{
adapter
=
ChatRoomAdapter
(
chatRoomType
,
chatRoomName
,
presenter
,
reactionListener
=
this
@ChatRoomFragment
)
adapter
=
ChatRoomAdapter
(
chatRoomType
,
chatRoomName
,
presenter
,
reactionListener
=
this
@ChatRoomFragment
)
recycler_view
.
adapter
=
adapter
if
(
dataSet
.
size
>=
30
)
{
recycler_view
.
addOnScrollListener
(
endlessRecyclerViewScrollListener
)
...
...
@@ -227,19 +289,32 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override
fun
onRoomUpdated
(
userCanPost
:
Boolean
,
channelIsBroadcast
:
Boolean
,
userCanMod
:
Boolean
)
{
// TODO: We should rely solely on the user being able to post, but we cannot guarantee
// that the "(channels|groups).roles" endpoint is supported by the server in use.
setupMessageComposer
(
userCanPost
)
isBroadcastChannel
=
channelIsBroadcast
if
(
isBroadcastChannel
&&
!
userCanMod
)
activity
?.
invalidateOptionsMenu
()
}
override
fun
openDirectMessage
(
chatRoom
:
ChatRoom
,
permalink
:
String
)
{
}
private
fun
toggleNoChatView
(
size
:
Int
)
{
if
(
size
==
0
){
if
(
size
==
0
)
{
image_chat_icon
.
setVisible
(
true
)
text_chat_title
.
setVisible
(
true
)
text_chat_description
.
setVisible
(
true
)
}
else
{
}
else
{
image_chat_icon
.
setVisible
(
false
)
text_chat_title
.
setVisible
(
false
)
text_chat_description
.
setVisible
(
false
)
}
}
private
val
layoutChangeListener
=
View
.
OnLayoutChangeListener
{
_
,
_
,
_
,
_
,
bottom
,
_
,
_
,
_
,
oldBottom
->
private
val
layoutChangeListener
=
View
.
OnLayoutChangeListener
{
_
,
_
,
_
,
_
,
bottom
,
_
,
_
,
_
,
oldBottom
->
val
y
=
oldBottom
-
bottom
if
(
Math
.
abs
(
y
)
>
0
&&
isAdded
)
{
// if y is positive the keyboard is up else it's down
...
...
@@ -287,7 +362,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if
(!
recyclerView
.
canScrollVertically
(
1
))
{
button_fab
.
hide
()
}
else
{
if
(
dy
<
0
&&
!
button_fab
.
isVisible
()
)
{
if
(
dy
<
0
&&
!
button_fab
.
isVisible
)
{
button_fab
.
show
()
}
}
...
...
@@ -306,6 +381,37 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override
fun
showTypingStatus
(
usernameList
:
ArrayList
<
String
>)
{
ui
{
when
(
usernameList
.
size
)
{
1
->
{
text_typing_status
.
text
=
SpannableStringBuilder
()
.
bold
{
append
(
usernameList
[
0
])
}
.
append
(
getString
(
R
.
string
.
msg_is_typing
))
}
2
->
{
text_typing_status
.
text
=
SpannableStringBuilder
()
.
bold
{
append
(
usernameList
[
0
])
}
.
append
(
getString
(
R
.
string
.
msg_and
))
.
bold
{
append
(
usernameList
[
1
])
}
.
append
(
getString
(
R
.
string
.
msg_are_typing
))
}
else
->
{
text_typing_status
.
text
=
getString
(
R
.
string
.
msg_several_users_are_typing
)
}
}
text_typing_status
.
isVisible
=
true
}
}
override
fun
hideTypingStatusView
()
{
ui
{
text_typing_status
.
isVisible
=
false
}
}
override
fun
uploadFile
(
uri
:
Uri
)
{
// TODO Just leaving a blank message that comes with the file for now. In the future lets add the possibility to add a message with the file to be uploaded.
presenter
.
uploadFile
(
chatRoomId
,
uri
,
""
)
...
...
@@ -363,19 +469,17 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override
fun
showReplyingAction
(
username
:
String
,
replyMarkdown
:
String
,
quotedMessage
:
String
)
{
override
fun
showReplyingAction
(
username
:
String
,
replyMarkdown
:
String
,
quotedMessage
:
String
)
{
ui
{
citation
=
replyMarkdown
actionSnackbar
.
title
=
username
actionSnackbar
.
text
=
quotedMessage
actionSnackbar
.
show
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
if
(!
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
verticalScrollOffset
.
set
(
0
)
}
}
}
}
...
...
@@ -508,21 +612,26 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
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
)
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
)
}
}
}
override
fun
onJoined
()
{
override
fun
onJoined
(
userCanPost
:
Boolean
)
{
ui
{
input_container
.
setVisible
(
true
)
button_join_chat
.
setVisible
(
false
)
isSubscribed
=
true
setupMessageComposer
()
setupMessageComposer
(
userCanPost
)
}
}
...
...
@@ -553,8 +662,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
fun
setupMessageComposer
()
{
if
(
isChatRoomReadOnly
)
{
private
fun
setupMessageComposer
(
canPost
:
Boolean
)
{
if
(
isChatRoomReadOnly
&&
!
canPost
)
{
text_room_is_read_only
.
setVisible
(
true
)
input_container
.
setVisible
(
false
)
}
else
if
(!
isSubscribed
)
{
...
...
@@ -567,8 +676,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_show_attachment_options
.
alpha
=
1f
button_show_attachment_options
.
setVisible
(
true
)
subscribeTextMessage
()
emojiKeyboardPopup
=
EmojiKeyboardPopup
(
activity
!!
,
activity
!!
.
findViewById
(
R
.
id
.
fragment_container
))
subscribeComposeTextMessage
()
emojiKeyboardPopup
=
EmojiKeyboardPopup
(
activity
!!
,
activity
!!
.
findViewById
(
R
.
id
.
fragment_container
))
emojiKeyboardPopup
.
listener
=
this
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
override
fun
onKeyboardOpened
()
{
...
...
@@ -658,7 +768,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
setReactionButtonIcon
(
R
.
drawable
.
ic_keyboard_black_24dp
)
}
else
{
// If popup is showing, simply dismiss it to show the undelying text keyboard
// If popup is showing, simply dismiss it to show the unde
r
lying text keyboard
emojiKeyboardPopup
.
dismiss
()
setReactionButtonIcon
(
R
.
drawable
.
ic_reaction_24dp
)
}
...
...
@@ -672,18 +782,30 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
})
}
private
fun
subscribeTextMessage
()
{
val
disposable
=
text_message
.
asObservable
(
0
)
.
subscribe
({
t
->
setupComposeMessageButtons
(
t
)
})
private
fun
subscribeComposeTextMessage
()
{
val
editTextObservable
=
text_message
.
asObservable
()
compositeDisposable
.
add
(
disposable
)
compositeDisposable
.
addAll
(
subscribeComposeButtons
(
editTextObservable
),
subscribeComposeTypingStatus
(
editTextObservable
)
)
}
private
fun
unsubscribeTextMessage
()
{
private
fun
unsubscribe
Compose
TextMessage
()
{
compositeDisposable
.
clear
()
}
private
fun
setupComposeMessageButtons
(
charSequence
:
CharSequence
)
{
private
fun
subscribeComposeButtons
(
observable
:
Observable
<
CharSequence
>):
Disposable
{
return
observable
.
subscribe
{
t
->
setupComposeButtons
(
t
)
}
}
private
fun
subscribeComposeTypingStatus
(
observable
:
Observable
<
CharSequence
>):
Disposable
{
return
observable
.
debounce
(
300
,
TimeUnit
.
MILLISECONDS
)
.
skip
(
1
)
.
subscribe
{
t
->
sendTypingStatus
(
t
)
}
}
private
fun
setupComposeButtons
(
charSequence
:
CharSequence
)
{
if
(
charSequence
.
isNotEmpty
()
&&
playComposeMessageButtonsAnimation
)
{
button_show_attachment_options
.
fadeOut
(
1F
,
0F
,
120
)
button_send
.
fadeIn
(
0F
,
1F
,
120
)
...
...
@@ -697,6 +819,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
fun
sendTypingStatus
(
charSequence
:
CharSequence
)
{
if
(
charSequence
.
isNotBlank
())
{
presenter
.
sendTyping
()
}
else
{
presenter
.
sendNotTyping
()
}
}
private
fun
showAttachmentOptions
()
{
view_dim
.
setVisible
(
true
)
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/bottomsheet/adapter/ActionListAdapter.kt
View file @
5e5e565d
...
...
@@ -7,8 +7,10 @@ import chat.rocket.android.util.extensions.setVisible
/**
* An adapter for bottomsheet menu that lists all the actions that could be taken over a chat message.
*/
class
ActionListAdapter
(
menuItems
:
List
<
MenuItem
>
=
emptyList
(),
callback
:
MenuItem
.
OnMenuItemClickListener
)
:
ListBottomSheetAdapter
(
menuItems
=
menuItems
,
callback
=
callback
)
{
class
ActionListAdapter
(
menuItems
:
List
<
MenuItem
>
=
emptyList
(),
callback
:
MenuItem
.
OnMenuItemClickListener
)
:
ListBottomSheetAdapter
(
menuItems
=
menuItems
,
callback
=
callback
)
{
override
fun
onBindViewHolder
(
holder
:
ViewHolder
,
position
:
Int
)
{
val
item
=
menuItems
[
position
]
...
...
@@ -25,7 +27,11 @@ class ActionListAdapter(menuItems: List<MenuItem> = emptyList(), callback: MenuI
callback
?.
onMenuItemClick
(
item
)
}
val
deleteTextColor
=
holder
.
itemView
.
context
.
resources
.
getColor
(
R
.
color
.
red
)
val
color
=
if
(
item
.
itemId
==
R
.
id
.
action_menu_msg_delete
)
deleteTextColor
else
textColors
.
get
(
item
.
itemId
)
val
color
=
if
(
item
.
itemId
==
R
.
id
.
action_message_delete
)
{
deleteTextColor
}
else
{
textColors
.
get
(
item
.
itemId
)
}
holder
.
textTitle
.
setTextColor
(
color
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
View file @
5e5e565d
...
...
@@ -24,7 +24,8 @@ interface BaseViewModel<out T> {
MESSAGE_ATTACHMENT
(
6
),
AUTHOR_ATTACHMENT
(
7
),
COLOR_ATTACHMENT
(
8
),
GENERIC_FILE_ATTACHMENT
(
9
)
GENERIC_FILE_ATTACHMENT
(
9
),
MESSAGE_REPLY
(
10
)
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageReplyViewModel.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.domain.MessageReply
import
chat.rocket.core.model.Message
data class
MessageReplyViewModel
(
override
val
rawData
:
MessageReply
,
override
val
messageId
:
String
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?,
override
var
preview
:
Message
?,
override
var
isTemporary
:
Boolean
=
false
,
override
val
message
:
Message
)
:
BaseViewModel
<
MessageReply
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE_REPLY
.
viewType
override
val
layoutId
:
Int
get
()
=
R
.
layout
.
item_message_reply
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/RoomViewModel.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.core.model.ChatRoomRole
data class
RoomViewModel
(
val
roles
:
List
<
ChatRoomRole
>,
val
isBroadcast
:
Boolean
=
false
,
val
isRoom
:
Boolean
=
false
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
5e5e565d
...
...
@@ -13,16 +13,32 @@ import androidx.core.text.buildSpannedString
import
androidx.core.text.color
import
androidx.core.text.scale
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.domain.MessageReply
import
chat.rocket.android.helper.MessageHelper
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.ChatRoomsInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.TokenRepository
import
chat.rocket.android.server.domain.baseUrl
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.isNotNullNorEmpty
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.attachment.*
import
chat.rocket.core.model.attachment.Attachment
import
chat.rocket.core.model.attachment.AudioAttachment
import
chat.rocket.core.model.attachment.AuthorAttachment
import
chat.rocket.core.model.attachment.ColorAttachment
import
chat.rocket.core.model.attachment.FileAttachment
import
chat.rocket.core.model.attachment.GenericFileAttachment
import
chat.rocket.core.model.attachment.ImageAttachment
import
chat.rocket.core.model.attachment.MessageAttachment
import
chat.rocket.core.model.attachment.VideoAttachment
import
chat.rocket.core.model.isSystemMessage
import
chat.rocket.core.model.url.Url
import
kotlinx.coroutines.experimental.CommonPool
...
...
@@ -34,6 +50,8 @@ import javax.inject.Inject
class
ViewModelMapper
@Inject
constructor
(
private
val
context
:
Context
,
private
val
parser
:
MessageParser
,
private
val
roomsInteractor
:
ChatRoomsInteractor
,
private
val
messageHelper
:
MessageHelper
,
tokenRepository
:
TokenRepository
,
serverInteractor
:
GetCurrentServerInteractor
,
getSettingsInteractor
:
GetSettingsInteractor
,
...
...
@@ -47,21 +65,24 @@ class ViewModelMapper @Inject constructor(
private
val
currentUsername
:
String
?
=
localRepository
.
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
private
val
secondaryTextColor
=
ContextCompat
.
getColor
(
context
,
R
.
color
.
colorSecondaryText
)
suspend
fun
map
(
message
:
Message
):
List
<
BaseViewModel
<*
>>
{
return
translate
(
message
)
suspend
fun
map
(
message
:
Message
,
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
roles
=
emptyList
(),
isBroadcast
=
true
)):
List
<
BaseViewModel
<*
>>
{
return
translate
(
message
,
roomViewModel
)
}
suspend
fun
map
(
messages
:
List
<
Message
>):
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
suspend
fun
map
(
messages
:
List
<
Message
>,
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
roles
=
emptyList
(),
isBroadcast
=
true
)):
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
val
list
=
ArrayList
<
BaseViewModel
<*>>(
messages
.
size
)
messages
.
forEach
{
list
.
addAll
(
translate
(
it
))
list
.
addAll
(
translate
(
it
,
roomViewModel
))
}
return
@withContext
list
}
private
suspend
fun
translate
(
message
:
Message
):
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
private
suspend
fun
translate
(
message
:
Message
,
roomViewModel
:
RoomViewModel
)
:
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
val
list
=
ArrayList
<
BaseViewModel
<*>>()
message
.
urls
?.
forEach
{
...
...
@@ -86,9 +107,40 @@ class ViewModelMapper @Inject constructor(
list
[
i
].
nextDownStreamMessage
=
next
}
if
(
isBroadcastReplyAvailable
(
roomViewModel
,
message
))
{
roomsInteractor
.
getById
(
currentServer
,
message
.
roomId
)
?.
let
{
chatRoom
->
val
replyViewModel
=
mapMessageReply
(
message
,
chatRoom
)
list
.
first
().
nextDownStreamMessage
=
replyViewModel
list
.
add
(
0
,
replyViewModel
)
}
}
return
@withContext
list
}
private
fun
isBroadcastReplyAvailable
(
roomViewModel
:
RoomViewModel
,
message
:
Message
):
Boolean
{
val
senderUsername
=
message
.
sender
?.
username
return
roomViewModel
.
isRoom
&&
roomViewModel
.
isBroadcast
&&
!
message
.
isSystemMessage
()
&&
senderUsername
!=
currentUsername
}
private
fun
mapMessageReply
(
message
:
Message
,
chatRoom
:
ChatRoom
):
MessageReplyViewModel
{
val
name
=
message
.
sender
?.
name
val
roomName
=
if
(
settings
.
useRealName
()
&&
name
!=
null
)
name
else
message
.
sender
?.
username
?:
""
val
permalink
=
messageHelper
.
createPermalink
(
message
,
chatRoom
)
return
MessageReplyViewModel
(
messageId
=
message
.
id
,
isTemporary
=
false
,
reactions
=
emptyList
(),
message
=
message
,
preview
=
mapMessagePreview
(
message
),
rawData
=
MessageReply
(
roomName
=
roomName
,
permalink
=
permalink
),
nextDownStreamMessage
=
null
)
}
private
fun
mapUrl
(
message
:
Message
,
url
:
Url
):
BaseViewModel
<
*
>?
{
if
(
url
.
ignoreParse
||
url
.
meta
==
null
)
return
null
...
...
@@ -322,6 +374,10 @@ class ViewModelMapper @Inject constructor(
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
->
context
.
getString
(
R
.
string
.
message_pinned
)
is
MessageType
.
UserMuted
->
context
.
getString
(
R
.
string
.
message_muted
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
UserUnMuted
->
context
.
getString
(
R
.
string
.
message_unmuted
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
SubscriptionRoleAdded
->
context
.
getString
(
R
.
string
.
message_role_add
,
message
.
message
,
message
.
role
,
message
.
sender
?.
username
)
is
MessageType
.
SubscriptionRoleRemoved
->
context
.
getString
(
R
.
string
.
message_role_removed
,
message
.
message
,
message
.
role
,
message
.
sender
?.
username
)
else
->
{
throw
InvalidParameterException
(
"Invalid message type: ${message.type}"
)
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
5e5e565d
package
chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.ChatRoomsSortOrder
import
chat.rocket.android.helper.Constants
import
chat.rocket.android.helper.SharedPreferenceHelper
import
chat.rocket.android.helper.UserHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.main.presentation.MainNavigator
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.ChatRoomsInteractor
import
chat.rocket.android.server.domain.GetActiveUsersInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.JobSchedulerInteractor
import
chat.rocket.android.server.domain.PermissionsInteractor
import
chat.rocket.android.server.domain.RefreshSettingsInteractor
import
chat.rocket.android.server.domain.SaveActiveUsersInteractor
import
chat.rocket.android.server.domain.SaveChatRoomsInteractor
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.hasShowLastMessage
import
chat.rocket.android.server.domain.showLastMessage
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.server.infraestructure.ConnectionManager
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.chatRooms
...
...
@@ -23,12 +37,18 @@ import chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.StreamMessage
import
chat.rocket.core.internal.realtime.socket.model.Type
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.permissions
import
chat.rocket.core.internal.rest.spotlight
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.Room
import
kotlinx.coroutines.experimental.*
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Deferred
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.delay
import
kotlinx.coroutines.experimental.launch
import
timber.log.Timber
import
javax.inject.Inject
import
kotlin.reflect.KProperty1
...
...
@@ -38,13 +58,16 @@ class ChatRoomsPresenter @Inject constructor(
private
val
strategy
:
CancelStrategy
,
private
val
navigator
:
MainNavigator
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
getChatRoomsInteractor
:
Get
ChatRoomsInteractor
,
private
val
chatRoomsInteractor
:
ChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
saveActiveUsersInteractor
:
SaveActiveUsersInteractor
,
private
val
getActiveUsersInteractor
:
GetActiveUsersInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
viewModelMapper
:
ViewModelMapper
,
private
val
jobSchedulerInteractor
:
JobSchedulerInteractor
,
private
val
permissionsInteractor
:
PermissionsInteractor
,
private
val
localRepository
:
LocalRepository
,
private
val
userHelper
:
UserHelper
,
settingsRepository
:
SettingsRepository
,
factory
:
ConnectionManagerFactory
)
{
...
...
@@ -69,6 +92,8 @@ class ChatRoomsPresenter @Inject constructor(
refreshSettingsInteractor
.
refresh
(
currentServer
)
}
view
.
updateChatRooms
(
getUserChatRooms
())
val
permissions
=
retryIO
{
client
.
permissions
()
}
permissionsInteractor
.
saveAll
(
permissions
)
}
catch
(
ex
:
RocketChatException
)
{
ex
.
message
?.
let
{
view
.
showMessage
(
it
)
...
...
@@ -85,7 +110,8 @@ class ChatRoomsPresenter @Inject constructor(
}
fun
loadChatRoom
(
chatRoom
:
ChatRoom
)
{
val
roomName
=
if
(
chatRoom
.
type
is
RoomType
.
DirectMessage
val
isDirectMessage
=
chatRoom
.
type
is
RoomType
.
DirectMessage
val
roomName
=
if
(
isDirectMessage
&&
chatRoom
.
fullName
!=
null
&&
settings
.
useRealName
())
{
chatRoom
.
fullName
!!
...
...
@@ -93,10 +119,40 @@ class ChatRoomsPresenter @Inject constructor(
chatRoom
.
name
}
launchUI
(
strategy
)
{
val
myself
=
getCurrentUser
()
if
(
myself
?.
username
==
null
)
{
view
.
showMessage
(
R
.
string
.
msg_generic_error
)
}
else
{
val
isChatRoomOwner
=
chatRoom
.
user
?.
username
==
myself
.
username
||
isDirectMessage
navigator
.
toChatRoom
(
chatRoom
.
id
,
roomName
,
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
,
chatRoom
.
lastSeen
?:
-
1
,
chatRoom
.
open
)
chatRoom
.
open
,
isChatRoomOwner
)
}
}
}
private
suspend
fun
getCurrentUser
():
User
?
{
userHelper
.
user
()
?.
let
{
return
it
}
try
{
val
myself
=
retryIO
{
client
.
me
()
}
val
user
=
User
(
id
=
myself
.
id
,
username
=
myself
.
username
,
name
=
myself
.
name
,
status
=
myself
.
status
,
utcOffset
=
myself
.
utcOffset
,
emails
=
null
,
roles
=
myself
.
roles
)
localRepository
.
saveCurrentUser
(
url
=
currentServer
,
user
=
user
)
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
}
return
null
}
/**
...
...
@@ -107,7 +163,7 @@ class ChatRoomsPresenter @Inject constructor(
val
currentServer
=
serverInteractor
.
get
()
!!
launchUI
(
strategy
)
{
try
{
val
roomList
=
getC
hatRoomsInteractor
.
getAllByName
(
currentServer
,
name
)
val
roomList
=
c
hatRoomsInteractor
.
getAllByName
(
currentServer
,
name
)
if
(
roomList
.
isEmpty
())
{
val
(
users
,
rooms
)
=
retryIO
(
"spotlight($name)"
)
{
client
.
spotlight
(
name
)
...
...
@@ -170,7 +226,8 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
null
,
groupMentions
=
0L
,
lastMessage
=
null
,
client
=
client
client
=
client
,
broadcast
=
false
)
}
}
...
...
@@ -203,14 +260,15 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
null
,
groupMentions
=
0L
,
lastMessage
=
it
.
lastMessage
,
client
=
client
client
=
client
,
broadcast
=
it
.
broadcast
)
}
}
fun
updateSortedChatRooms
()
{
launchUI
(
strategy
)
{
val
roomList
=
getC
hatRoomsInteractor
.
getAll
(
currentServer
)
val
roomList
=
c
hatRoomsInteractor
.
getAll
(
currentServer
)
view
.
updateChatRooms
(
sortRooms
(
roomList
))
}
}
...
...
@@ -294,7 +352,8 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
it
.
userMentions
,
groupMentions
=
it
.
groupMentions
,
lastMessage
=
it
.
lastMessage
,
client
=
client
client
=
client
,
broadcast
=
it
.
broadcast
)
chatRoomsList
.
add
(
newRoom
)
}
...
...
@@ -409,13 +468,13 @@ class ChatRoomsPresenter @Inject constructor(
// Update a ChatRoom with a Room information
private
fun
updateRoom
(
room
:
Room
)
{
Timber
.
d
(
"Updating Room: ${room.id} - ${room.name}"
)
val
chatRooms
=
getC
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
val
chatRooms
=
c
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
room
.
id
}
chatRoom
?.
apply
{
val
newRoom
=
ChatRoom
(
id
=
room
.
id
,
type
=
room
.
type
,
user
=
room
.
user
?:
user
,
user
=
room
.
user
,
status
=
getActiveUsersInteractor
.
getActiveUserByUsername
(
currentServer
,
room
.
name
?:
name
...
...
@@ -437,7 +496,8 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
userMentions
,
groupMentions
=
groupMentions
,
lastMessage
=
room
.
lastMessage
,
client
=
client
client
=
client
,
broadcast
=
broadcast
)
removeRoom
(
room
.
id
,
chatRooms
)
chatRooms
.
add
(
newRoom
)
...
...
@@ -448,13 +508,13 @@ class ChatRoomsPresenter @Inject constructor(
// Update a ChatRoom with a Subscription information
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
Timber
.
d
(
"Updating subscription: ${subscription.id} - ${subscription.name}"
)
val
chatRooms
=
getC
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
val
chatRooms
=
c
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
chatRoom
?.
apply
{
val
newRoom
=
ChatRoom
(
id
=
subscription
.
roomId
,
type
=
subscription
.
type
,
user
=
subscription
.
user
?:
user
,
user
=
user
,
status
=
getActiveUsersInteractor
.
getActiveUserByUsername
(
currentServer
,
subscription
.
name
...
...
@@ -476,7 +536,8 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
subscription
.
userMentions
,
groupMentions
=
subscription
.
groupMentions
,
lastMessage
=
lastMessage
,
client
=
client
client
=
client
,
broadcast
=
broadcast
)
removeRoom
(
subscription
.
roomId
,
chatRooms
)
chatRooms
.
add
(
newRoom
)
...
...
@@ -486,7 +547,7 @@ class ChatRoomsPresenter @Inject constructor(
private
fun
removeRoom
(
id
:
String
,
chatRooms
:
MutableList
<
ChatRoom
>
=
getC
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
chatRooms
:
MutableList
<
ChatRoom
>
=
c
hatRoomsInteractor
.
getAll
(
currentServer
).
toMutableList
()
)
{
Timber
.
d
(
"Removing ROOM: $id"
)
synchronized
(
this
)
{
...
...
@@ -527,7 +588,7 @@ class ChatRoomsPresenter @Inject constructor(
val
username
=
user_
.
username
val
status
=
user_
.
status
if
(
username
!=
null
&&
status
!=
null
)
{
getC
hatRoomsInteractor
.
getByName
(
currentServer
,
username
)
?.
let
{
c
hatRoomsInteractor
.
getByName
(
currentServer
,
username
)
?.
let
{
val
newRoom
=
ChatRoom
(
id
=
it
.
id
,
type
=
it
.
type
,
...
...
@@ -550,13 +611,14 @@ class ChatRoomsPresenter @Inject constructor(
userMentions
=
it
.
userMentions
,
groupMentions
=
it
.
groupMentions
,
lastMessage
=
it
.
lastMessage
,
client
=
client
client
=
client
,
broadcast
=
it
.
broadcast
)
getC
hatRoomsInteractor
.
remove
(
currentServer
,
it
)
getC
hatRoomsInteractor
.
add
(
currentServer
,
newRoom
)
c
hatRoomsInteractor
.
remove
(
currentServer
,
it
)
c
hatRoomsInteractor
.
add
(
currentServer
,
newRoom
)
launchUI
(
strategy
)
{
view
.
updateChatRooms
(
sortRooms
(
getC
hatRoomsInteractor
.
getAll
(
currentServer
)))
view
.
updateChatRooms
(
sortRooms
(
c
hatRoomsInteractor
.
getAll
(
currentServer
)))
}
}
}
...
...
@@ -566,7 +628,7 @@ class ChatRoomsPresenter @Inject constructor(
Timber
.
i
(
"Updating ChatRooms"
)
launch
(
strategy
.
jobs
)
{
val
chatRoomsWithPreview
=
getChatRoomsWithPreviews
(
getC
hatRoomsInteractor
.
getAll
(
currentServer
)
c
hatRoomsInteractor
.
getAll
(
currentServer
)
)
val
chatRoomsWithStatus
=
getChatRoomWithStatus
(
chatRoomsWithPreview
)
view
.
updateChatRooms
(
chatRoomsWithStatus
)
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
View file @
5e5e565d
...
...
@@ -25,10 +25,13 @@ import com.facebook.drawee.view.SimpleDraweeView
import
kotlinx.android.synthetic.main.item_chat.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
localRepository
:
LocalRepository
,
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
ViewHolder
=
ViewHolder
(
parent
.
inflate
(
R
.
layout
.
item_chat
))
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
5e5e565d
...
...
@@ -14,6 +14,7 @@ import android.support.v7.widget.SearchView
import
android.view.*
import
android.widget.CheckBox
import
android.widget.RadioGroup
import
androidx.core.view.isVisible
import
chat.rocket.android.R
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
...
...
@@ -23,7 +24,6 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.showLastMessage
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.common.model.RoomType
...
...
@@ -37,11 +37,14 @@ import timber.log.Timber
import
javax.inject.Inject
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
settingsRepository
:
SettingsRepository
@Inject
lateinit
var
localRepository
:
LocalRepository
private
lateinit
var
preferences
:
SharedPreferences
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
settingsRepository
:
SettingsRepository
@Inject
lateinit
var
localRepository
:
LocalRepository
private
var
searchView
:
SearchView
?
=
null
private
val
handler
=
Handler
()
...
...
@@ -56,7 +59,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
setHasOptionsMenu
(
true
)
preferences
=
context
?.
getSharedPreferences
(
"temp"
,
Context
.
MODE_PRIVATE
)
!!
}
override
fun
onDestroy
()
{
...
...
@@ -146,9 +148,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
return
super
.
onOptionsItemSelected
(
item
)
}
private
fun
invalidateQueryOnSearch
(){
private
fun
invalidateQueryOnSearch
()
{
searchView
?.
let
{
if
(!
searchView
!!
.
isIconified
){
if
(!
searchView
!!
.
isIconified
)
{
queryChatRoomsByName
(
searchView
!!
.
query
.
toString
())
}
}
...
...
@@ -163,7 +165,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
/*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await()*/
text_no_search
.
isVisible
=
newDataSet
.
isEmpty
()
if
(
isActive
)
{
adapter
.
baseAdapter
.
updateRooms
(
newDataSet
)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
...
...
@@ -179,7 +181,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
ui
{
text_no_data_to_display
.
setVisible
(
true
)
}
}
override
fun
showLoading
(){
override
fun
showLoading
()
{
ui
{
view_loading
.
setVisible
(
true
)
}
}
...
...
@@ -241,10 +243,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
// TODO - use a ViewModel Mapper instead of using settings on the adapter
println
(
serverInteractor
.
get
()
+
" -> ${settingsRepository.get(serverInteractor.get()!!).showLastMessage()}"
)
val
baseAdapter
=
ChatRoomsAdapter
(
it
,
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
),
localRepository
)
{
chatRoom
->
presenter
.
loadChatRoom
(
chatRoom
)
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
),
localRepository
)
{
chatRoom
->
presenter
.
loadChatRoom
(
chatRoom
)
}
sectionedAdapter
=
SimpleSectionedRecyclerViewAdapter
(
it
,
...
...
app/src/main/java/chat/rocket/android/dagger/module/ActivityBuilder.kt
View file @
5e5e565d
...
...
@@ -10,6 +10,7 @@ import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import
chat.rocket.android.authentication.ui.AuthenticationActivity
import
chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
import
chat.rocket.android.chatroom.di.ChatRoomModule
import
chat.rocket.android.chatroom.di.FavoriteMessagesFragmentProvider
import
chat.rocket.android.chatroom.di.PinnedMessagesFragmentProvider
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.chatrooms.di.ChatRoomsFragmentProvider
...
...
@@ -47,10 +48,15 @@ abstract class ActivityBuilder {
abstract
fun
bindMainActivity
():
MainActivity
@PerActivity
@ContributesAndroidInjector
(
modules
=
[
ChatRoomModule
::
class
,
@ContributesAndroidInjector
(
modules
=
[
ChatRoomModule
::
class
,
ChatRoomFragmentProvider
::
class
,
MembersFragmentProvider
::
class
,
PinnedMessagesFragmentProvider
::
class
])
PinnedMessagesFragmentProvider
::
class
,
FavoriteMessagesFragmentProvider
::
class
]
)
abstract
fun
bindChatRoomActivity
():
ChatRoomActivity
@PerActivity
...
...
app/src/main/java/chat/rocket/android/dagger/module/AppModule.kt
View file @
5e5e565d
...
...
@@ -14,12 +14,10 @@ import chat.rocket.android.app.RocketChatDatabase
import
chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import
chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.dagger.qualifier.ForFresco
import
chat.rocket.android.dagger.qualifier.ForMessages
import
chat.rocket.android.helper.FrescoAuthInterceptor
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import
chat.rocket.android.infrastructure.SharedPref
erence
sLocalRepository
import
chat.rocket.android.push.GroupedPush
import
chat.rocket.android.push.PushManager
import
chat.rocket.android.server.domain.*
...
...
@@ -43,7 +41,6 @@ import com.squareup.moshi.Moshi
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.experimental.Job
import
okhttp3.Interceptor
import
okhttp3.OkHttpClient
import
okhttp3.logging.HttpLoggingInterceptor
import
ru.noties.markwon.SpannableConfiguration
...
...
@@ -119,24 +116,8 @@ class AppModule {
}
@Provides
@ForFresco
@Singleton
fun
provideFrescoAuthInterceptor
(
tokenRepository
:
TokenRepository
,
currentServerInteractor
:
GetCurrentServerInteractor
):
Interceptor
{
return
FrescoAuthInterceptor
(
tokenRepository
,
currentServerInteractor
)
}
@Provides
@ForFresco
@Singleton
fun
provideFrescoOkHttpClient
(
okHttpClient
:
OkHttpClient
,
@ForFresco
authInterceptor
:
Interceptor
):
OkHttpClient
{
return
okHttpClient
.
newBuilder
().
apply
{
//addInterceptor(authInterceptor)
}.
build
()
}
@Provides
@Singleton
fun
provideImagePipelineConfig
(
context
:
Context
,
@ForFresco
okHttpClient
:
OkHttpClient
):
ImagePipelineConfig
{
fun
provideImagePipelineConfig
(
context
:
Context
,
okHttpClient
:
OkHttpClient
):
ImagePipelineConfig
{
val
listeners
=
setOf
(
RequestLoggingListener
())
return
OkHttpImagePipelineConfigFactory
.
newBuilder
(
context
,
okHttpClient
)
...
...
@@ -177,8 +158,8 @@ class AppModule {
@Provides
@Singleton
fun
provideLocalRepository
(
prefs
:
SharedPreferences
):
LocalRepository
{
return
SharedPref
sLocalRepository
(
prefs
)
fun
provideLocalRepository
(
prefs
:
SharedPreferences
,
moshi
:
Moshi
):
LocalRepository
{
return
SharedPref
erencesLocalRepository
(
prefs
,
moshi
)
}
@Provides
...
...
@@ -193,6 +174,12 @@ class AppModule {
return
SharedPreferencesSettingsRepository
(
localRepository
)
}
@Provides
@Singleton
fun
providePermissionsRepository
(
localRepository
:
LocalRepository
,
moshi
:
Moshi
):
PermissionsRepository
{
return
SharedPreferencesPermissionsRepository
(
localRepository
,
moshi
)
}
@Provides
@Singleton
fun
provideRoomRepository
():
RoomRepository
{
...
...
@@ -258,7 +245,7 @@ class AppModule {
@Provides
@Singleton
fun
provideConfiguration
(
context
:
Application
,
client
:
OkHttpClient
):
SpannableConfiguration
{
fun
provideConfiguration
(
context
:
Application
):
SpannableConfiguration
{
val
res
=
context
.
resources
return
SpannableConfiguration
.
builder
(
context
)
.
theme
(
SpannableTheme
.
builder
()
...
...
@@ -273,12 +260,6 @@ class AppModule {
return
MessageParser
(
context
,
configuration
,
settingsInteractor
.
get
(
url
))
}
@Provides
@Singleton
fun
providePermissionInteractor
(
settingsRepository
:
SettingsRepository
,
serverRepository
:
CurrentServerRepository
):
GetPermissionsInteractor
{
return
GetPermissionsInteractor
(
settingsRepository
,
serverRepository
)
}
@Provides
@Singleton
fun
provideAccountsRepository
(
preferences
:
SharedPreferences
,
moshi
:
Moshi
):
AccountsRepository
=
...
...
app/src/main/java/chat/rocket/android/dagger/module/LocalModule.kt
View file @
5e5e565d
...
...
@@ -3,7 +3,21 @@ package chat.rocket.android.dagger.module
import
android.content.Context
import
android.content.SharedPreferences
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import
chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
import
chat.rocket.android.server.domain.CurrentServerRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import
chat.rocket.android.util.AppJsonAdapterFactory
import
chat.rocket.android.util.TimberLogger
import
chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import
chat.rocket.common.internal.ISO8601Date
import
chat.rocket.common.model.TimestampAdapter
import
chat.rocket.common.util.CalendarISO8601Converter
import
chat.rocket.common.util.Logger
import
chat.rocket.common.util.PlatformLogger
import
chat.rocket.core.internal.AttachmentAdapterFactory
import
chat.rocket.core.internal.ReactionsAdapter
import
com.squareup.moshi.Moshi
import
dagger.Module
import
dagger.Provides
import
javax.inject.Singleton
...
...
@@ -11,14 +25,53 @@ import javax.inject.Singleton
@Module
class
LocalModule
{
@Provides
@Singleton
fun
providePlatformLogger
():
PlatformLogger
{
return
TimberLogger
}
@Provides
@Singleton
fun
provideCurrentServerRepository
(
prefs
:
SharedPreferences
):
CurrentServerRepository
{
return
SharedPrefsCurrentServerRepository
(
prefs
)
}
@Provides
@Singleton
fun
provideMoshi
(
logger
:
PlatformLogger
,
currentServerInteractor
:
GetCurrentServerInteractor
):
Moshi
{
val
url
=
currentServerInteractor
.
get
()
?:
""
return
Moshi
.
Builder
()
.
add
(
FallbackSealedClassJsonAdapter
.
ADAPTER_FACTORY
)
.
add
(
AppJsonAdapterFactory
.
INSTANCE
)
.
add
(
AttachmentAdapterFactory
(
Logger
(
logger
,
url
)))
.
add
(
java
.
lang
.
Long
::
class
.
java
,
ISO8601Date
::
class
.
java
,
TimestampAdapter
(
CalendarISO8601Converter
())
)
.
add
(
Long
::
class
.
java
,
ISO8601Date
::
class
.
java
,
TimestampAdapter
(
CalendarISO8601Converter
())
)
.
add
(
ReactionsAdapter
())
.
build
()
}
@Provides
fun
provideSharedPreferences
(
context
:
Context
):
SharedPreferences
{
return
context
.
getSharedPreferences
(
"rocket.chat"
,
Context
.
MODE_PRIVATE
)
}
@Provides
@Singleton
fun
provideLocalRepository
(
prefs
:
SharedPreferences
):
LocalRepository
{
return
SharedPref
sLocalRepository
(
prefs
)
fun
provideLocalRepository
(
sharedPreferences
:
SharedPreferences
,
moshi
:
Moshi
):
LocalRepository
{
return
SharedPref
erencesLocalRepository
(
sharedPreferences
,
moshi
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/dagger/qualifier/ForFresco.kt
deleted
100644 → 0
View file @
b0817ace
package
chat.rocket.android.dagger.qualifier
import
javax.inject.Qualifier
@Qualifier
@Retention
(
AnnotationRetention
.
RUNTIME
)
annotation
class
ForFresco
\ No newline at end of file
app/src/main/java/chat/rocket/android/favoritemessages/di/FavoriteMessagesFragmentModule.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.di
import
android.arch.lifecycle.LifecycleOwner
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import
chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class
FavoriteMessagesFragmentModule
{
@Provides
fun
provideLifecycleOwner
(
frag
:
FavoriteMessagesFragment
):
LifecycleOwner
{
return
frag
}
@Provides
fun
provideCancelStrategy
(
owner
:
LifecycleOwner
,
jobs
:
Job
):
CancelStrategy
{
return
CancelStrategy
(
owner
,
jobs
)
}
@Provides
fun
provideFavoriteMessagesView
(
frag
:
FavoriteMessagesFragment
):
FavoriteMessagesView
{
return
frag
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/favoritemessages/di/FavoriteMessagesFragmentProvider.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.chatroom.di
import
chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
@Module
abstract
class
FavoriteMessagesFragmentProvider
{
@ContributesAndroidInjector
(
modules
=
[
FavoriteMessagesFragmentModule
::
class
])
abstract
fun
provideFavoriteMessageFragment
():
FavoriteMessagesFragment
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.favoritemessages.presentation
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.ChatRoomsInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.rest.getFavoriteMessages
import
timber.log.Timber
import
javax.inject.Inject
class
FavoriteMessagesPresenter
@Inject
constructor
(
private
val
view
:
FavoriteMessagesView
,
private
val
strategy
:
CancelStrategy
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
roomsInteractor
:
ChatRoomsInteractor
,
private
val
mapper
:
ViewModelMapper
,
factory
:
RocketChatClientFactory
)
{
private
val
client
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
var
offset
:
Int
=
0
/**
* Loads all favorite messages for room. the given room id.
*
* @param roomId The id of the room to get its favorite messages.
*/
fun
loadFavoriteMessages
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
try
{
val
serverUrl
=
serverInteractor
.
get
()
!!
val
chatRoom
=
roomsInteractor
.
getById
(
serverUrl
,
roomId
)
chatRoom
?.
let
{
room
->
view
.
showLoading
()
val
favoriteMessages
=
client
.
getFavoriteMessages
(
roomId
,
room
.
type
,
offset
)
offset
=
favoriteMessages
.
offset
.
toInt
()
val
messageList
=
mapper
.
map
(
favoriteMessages
.
result
)
view
.
showFavoriteMessages
(
messageList
)
view
.
hideLoading
()
}.
ifNull
{
Timber
.
e
(
"Couldn't find a room with id: $roomId at current server."
)
}
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesView.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.favoritemessages.presentation
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
interface
FavoriteMessagesView
:
MessageView
,
LoadingView
{
/**
* Shows the list of favorite messages for the current room.
*
* @param favoriteMessages The list of favorite messages to show.
*/
fun
showFavoriteMessages
(
favoriteMessages
:
List
<
BaseViewModel
<*
>>)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.favoritemessages.ui
import
android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v7.widget.DefaultItemAnimator
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
androidx.core.view.isVisible
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.favoritemessages.presentation.FavoriteMessagesPresenter
import
chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import
chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.ui
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_favorite_messages.*
import
javax.inject.Inject
fun
newInstance
(
chatRoomId
:
String
,
chatRoomType
:
String
):
Fragment
{
return
FavoriteMessagesFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
putString
(
INTENT_CHAT_ROOM_ID
,
chatRoomId
)
putString
(
INTENT_CHAT_ROOM_TYPE
,
chatRoomType
)
}
}
}
private
const
val
INTENT_CHAT_ROOM_ID
=
"chat_room_id"
private
const
val
INTENT_CHAT_ROOM_TYPE
=
"chat_room_type"
class
FavoriteMessagesFragment
:
Fragment
(),
FavoriteMessagesView
{
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomType
:
String
private
lateinit
var
adapter
:
ChatRoomAdapter
@Inject
lateinit
var
presenter
:
FavoriteMessagesPresenter
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
val
bundle
=
arguments
if
(
bundle
!=
null
)
{
chatRoomId
=
bundle
.
getString
(
INTENT_CHAT_ROOM_ID
)
chatRoomType
=
bundle
.
getString
(
INTENT_CHAT_ROOM_TYPE
)
}
else
{
requireNotNull
(
bundle
)
{
"no arguments supplied when the fragment was instantiated"
}
}
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
):
View
?
=
container
?.
inflate
(
R
.
layout
.
fragment_favorite_messages
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
setupToolbar
()
presenter
.
loadFavoriteMessages
(
chatRoomId
)
}
override
fun
showFavoriteMessages
(
favoriteMessages
:
List
<
BaseViewModel
<*
>>)
{
ui
{
if
(
recycler_view
.
adapter
==
null
)
{
adapter
=
ChatRoomAdapter
(
chatRoomType
,
""
,
null
,
false
)
recycler_view
.
adapter
=
adapter
val
linearLayoutManager
=
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
recycler_view
.
layoutManager
=
linearLayoutManager
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
if
(
favoriteMessages
.
size
>
10
)
{
recycler_view
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?
)
{
presenter
.
loadFavoriteMessages
(
chatRoomId
)
}
})
}
no_messages_view
.
isVisible
=
favoriteMessages
.
isEmpty
()
}
adapter
.
appendData
(
favoriteMessages
)
}
}
override
fun
showMessage
(
resId
:
Int
)
{
ui
{
showToast
(
resId
)
}
}
override
fun
showMessage
(
message
:
String
)
{
ui
{
showToast
(
message
)
}
}
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showLoading
()
{
ui
{
view_loading
.
isVisible
=
true
}
}
override
fun
hideLoading
()
{
ui
{
view_loading
.
isVisible
=
false
}
}
private
fun
setupToolbar
()
{
(
activity
as
ChatRoomActivity
).
setupToolbarTitle
(
getString
(
R
.
string
.
title_favorite_messages
))
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/helper/FrescoAuthInterceptor.kt
deleted
100644 → 0
View file @
b0817ace
package
chat.rocket.android.helper
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.TokenRepository
import
okhttp3.Interceptor
import
okhttp3.Response
class
FrescoAuthInterceptor
(
private
val
tokenRepository
:
TokenRepository
,
private
val
currentServerInteractor
:
GetCurrentServerInteractor
)
:
Interceptor
{
override
fun
intercept
(
chain
:
Interceptor
.
Chain
):
Response
{
var
request
=
chain
.
request
()
currentServerInteractor
.
get
()
?.
let
{
serverUrl
->
val
token
=
tokenRepository
.
get
(
serverUrl
)
return
@let
token
?.
let
{
val
url
=
request
.
url
().
newBuilder
().
apply
{
addQueryParameter
(
"rc_uid"
,
token
.
userId
)
addQueryParameter
(
"rc_token"
,
token
.
authToken
)
}.
build
()
request
=
request
.
newBuilder
().
apply
{
url
(
url
)
}.
build
()
}
}
return
chain
.
proceed
(
request
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/helper/MessageHelper.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.helper
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.common.model.RoomType
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.Message
import
javax.inject.Inject
class
MessageHelper
@Inject
constructor
(
getSettingsInteractor
:
GetSettingsInteractor
,
serverInteractor
:
GetCurrentServerInteractor
)
{
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
settings
:
PublicSettings
=
getSettingsInteractor
.
get
(
currentServer
)
fun
createPermalink
(
message
:
Message
,
chatRoom
:
ChatRoom
):
String
{
val
type
=
when
(
chatRoom
.
type
)
{
is
RoomType
.
PrivateGroup
->
"group"
is
RoomType
.
Channel
->
"channel"
is
RoomType
.
DirectMessage
->
"direct"
is
RoomType
.
Livechat
->
"livechat"
else
->
"custom"
}
val
name
=
if
(
settings
.
useRealName
())
chatRoom
.
fullName
?:
chatRoom
.
name
else
chatRoom
.
name
return
"[ ]($currentServer/$type/$name?msg=${message.id}) "
}
fun
messageIdFromPermalink
(
permalink
:
String
):
String
?
{
PERMALINK_REGEX
.
find
(
permalink
.
trim
())
?.
let
{
if
(
it
.
groupValues
.
size
==
5
)
{
return
it
.
groupValues
[
MESSAGE_ID
]
}
}
return
null
}
fun
roomNameFromPermalink
(
permalink
:
String
):
String
?
{
PERMALINK_REGEX
.
find
(
permalink
.
trim
())
?.
let
{
if
(
it
.
groupValues
.
size
==
5
)
{
return
it
.
groupValues
[
ROOM_NAME
]
}
}
return
null
}
fun
roomTypeFromPermalink
(
permalink
:
String
):
String
?
{
PERMALINK_REGEX
.
find
(
permalink
.
trim
())
?.
let
{
if
(
it
.
groupValues
.
size
==
5
)
{
val
type
=
it
.
groupValues
[
ROOM_TYPE
]
return
when
(
type
)
{
"group"
->
"p"
"channel"
->
"c"
"direct"
->
"d"
"livechat"
->
"l"
else
->
type
}
}
}
return
null
}
companion
object
{
private
const
val
ROOM_TYPE
=
2
private
const
val
ROOM_NAME
=
3
private
const
val
MESSAGE_ID
=
4
val
PERMALINK_REGEX
=
"(?:__|[*#])|\\[(.+?)\\]\\(.+?//.+?/(.+)/(.+)\\?.*=(.*)\\)"
.
toRegex
()
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/helper/UserHelper.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.helper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.common.model.User
import
javax.inject.Inject
class
UserHelper
@Inject
constructor
(
private
val
localRepository
:
LocalRepository
,
private
val
getCurrentServerInteractor
:
GetCurrentServerInteractor
,
settingsRepository
:
SettingsRepository
)
{
private
val
settings
:
PublicSettings
=
settingsRepository
.
get
(
getCurrentServerInteractor
.
get
()
!!
)
/**
* Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, or else
* the username without the '@' is yielded. The fallback for any case is the username, which
* could be null.
*/
fun
displayName
(
user
:
User
):
String
?
{
return
if
(
settings
.
useRealName
())
user
.
name
?:
user
.
username
else
user
.
username
}
/**
* Return current logged user's display name.
*
* @see displayName
*/
fun
displayName
():
String
?
{
user
()
?.
let
{
return
displayName
(
it
)
}
return
null
}
/**
* Return current logged [User].
*/
fun
user
():
User
?
{
return
localRepository
.
getCurrentUser
(
serverUrl
())
}
/**
* Return the username for the current logged [User].
*/
fun
username
():
String
?
=
localRepository
.
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
,
null
)
/**
* Whether current [User] is admin on the current server.
*/
fun
isAdmin
():
Boolean
{
return
user
()
?.
roles
?.
find
{
it
.
equals
(
"admin"
,
ignoreCase
=
true
)
}
!=
null
}
private
fun
serverUrl
():
String
{
return
getCurrentServerInteractor
.
get
()
!!
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt
View file @
5e5e565d
package
chat.rocket.android.infrastructure
import
chat.rocket.common.model.User
interface
LocalRepository
{
fun
save
(
key
:
String
,
value
:
String
?)
...
...
@@ -14,12 +16,16 @@ interface LocalRepository {
fun
getLong
(
key
:
String
,
defValue
:
Long
=
-
1L
):
Long
fun
clear
(
key
:
String
)
fun
clearAllFromServer
(
server
:
String
)
fun
getCurrentUser
(
url
:
String
):
User
?
fun
saveCurrentUser
(
url
:
String
,
user
:
User
)
companion
object
{
const
val
KEY_PUSH_TOKEN
=
"KEY_PUSH_TOKEN"
const
val
MIGRATION_FINISHED_KEY
=
"MIGRATION_FINISHED_KEY"
const
val
TOKEN_KEY
=
"token_"
const
val
SETTINGS_KEY
=
"settings_"
const
val
PERMISSIONS_KEY
=
"permissions_"
const
val
USER_KEY
=
"user_"
const
val
CURRENT_USERNAME_KEY
=
"username_"
}
}
...
...
app/src/main/java/chat/rocket/android/infrastructure/SharedPrefsLocalRepository.kt
→
app/src/main/java/chat/rocket/android/infrastructure/SharedPref
erence
sLocalRepository.kt
View file @
5e5e565d
...
...
@@ -2,8 +2,26 @@ package chat.rocket.android.infrastructure
import
android.content.SharedPreferences
import
androidx.core.content.edit
import
chat.rocket.common.model.User
import
com.squareup.moshi.Moshi
class
SharedPreferencesLocalRepository
(
private
val
preferences
:
SharedPreferences
,
moshi
:
Moshi
)
:
LocalRepository
{
private
val
userAdapter
=
moshi
.
adapter
(
User
::
class
.
java
)
override
fun
getCurrentUser
(
url
:
String
):
User
?
{
return
get
(
"${url}_${LocalRepository.USER_KEY}"
,
null
)
?.
let
{
userAdapter
.
fromJson
(
it
)
}
}
override
fun
saveCurrentUser
(
url
:
String
,
user
:
User
)
{
save
(
"${url}_${LocalRepository.USER_KEY}"
,
userAdapter
.
toJson
(
user
))
}
class
SharedPrefsLocalRepository
(
private
val
preferences
:
SharedPreferences
)
:
LocalRepository
{
override
fun
getBoolean
(
key
:
String
,
defValue
:
Boolean
)
=
preferences
.
getBoolean
(
key
,
defValue
)
override
fun
getFloat
(
key
:
String
,
defValue
:
Float
)
=
preferences
.
getFloat
(
key
,
defValue
)
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt
View file @
5e5e565d
package
chat.rocket.android.main.presentation
import
android.content.Context
import
chat.rocket.android.R
import
chat.rocket.android.authentication.ui.newServerIntent
import
chat.rocket.android.chatroom.ui.chatRoomIntent
...
...
@@ -36,9 +35,10 @@ class MainNavigator(internal val activity: MainActivity) {
chatRoomType
:
String
,
isChatRoomReadOnly
:
Boolean
,
chatRoomLastSeen
:
Long
,
isChatRoomSubscribed
:
Boolean
)
{
isChatRoomSubscribed
:
Boolean
,
isChatRoomCreator
:
Boolean
)
{
activity
.
startActivity
(
activity
.
chatRoomIntent
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
isChatRoomReadOnly
,
chatRoomLastSeen
,
isChatRoomSubscribed
))
isChatRoomReadOnly
,
chatRoomLastSeen
,
isChatRoomSubscribed
,
isChatRoomCreator
))
activity
.
overridePendingTransition
(
R
.
anim
.
open_enter
,
R
.
anim
.
open_exit
)
}
...
...
app/src/main/java/chat/rocket/android/main/viewmodel/NavHeaderViewModel.kt
View file @
5e5e565d
...
...
@@ -2,7 +2,6 @@ package chat.rocket.android.main.viewmodel
import
chat.rocket.common.model.UserStatus
data class
NavHeaderViewModel
(
val
userDisplayName
:
String
?,
val
userStatus
:
UserStatus
?,
...
...
app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt
View file @
5e5e565d
...
...
@@ -6,7 +6,6 @@ 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.MenuItem
import
android.view.View
import
android.view.ViewGroup
import
chat.rocket.android.R
...
...
@@ -38,9 +37,12 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
private
const
val
BUNDLE_CHAT_ROOM_TYPE
=
"chat_room_type"
class
MembersFragment
:
Fragment
(),
MembersView
{
@Inject
lateinit
var
presenter
:
MembersPresenter
private
val
adapter
:
MembersAdapter
=
MembersAdapter
{
memberViewModel
->
presenter
.
toMemberDetails
(
memberViewModel
)
}
private
val
linearLayoutManager
=
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
@Inject
lateinit
var
presenter
:
MembersPresenter
private
val
adapter
:
MembersAdapter
=
MembersAdapter
{
memberViewModel
->
presenter
.
toMemberDetails
(
memberViewModel
)
}
private
val
linearLayoutManager
=
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomType
:
String
...
...
@@ -58,13 +60,15 @@ class MembersFragment : Fragment(), MembersView {
}
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
=
container
?.
inflate
(
R
.
layout
.
fragment_members
)
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
):
View
?
=
container
?.
inflate
(
R
.
layout
.
fragment_members
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
""
setupRecyclerView
()
presenter
.
loadChatRoomsMembers
(
chatRoomId
,
chatRoomType
)
}
...
...
@@ -75,8 +79,13 @@ class MembersFragment : Fragment(), MembersView {
if
(
adapter
.
itemCount
==
0
)
{
adapter
.
prependData
(
dataSet
)
if
(
dataSet
.
size
>=
59
)
{
// TODO Check why the API retorns the specified count -1
recycler_view
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?)
{
recycler_view
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?
)
{
presenter
.
loadChatRoomsMembers
(
chatRoomId
,
chatRoomType
,
page
*
60L
)
}
})
...
...
@@ -84,18 +93,7 @@ class MembersFragment : Fragment(), MembersView {
}
else
{
adapter
.
appendData
(
dataSet
)
}
if
(
it
is
ChatRoomActivity
)
{
it
.
showRoomTypeIcon
(
false
)
}
}
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
if
(
item
.
itemId
==
android
.
R
.
id
.
home
)
{
(
activity
as
ChatRoomActivity
).
showRoomTypeIcon
(
true
)
return
super
.
onOptionsItemSelected
(
item
)
}
return
super
.
onOptionsItemSelected
(
item
)
}
override
fun
showLoading
()
{
...
...
@@ -129,6 +127,8 @@ class MembersFragment : Fragment(), MembersView {
}
private
fun
setupToolbar
(
totalMembers
:
Long
)
{
(
activity
as
ChatRoomActivity
?)
?.
setupToolbarTitle
(
getString
(
R
.
string
.
title_members
,
totalMembers
))
(
activity
as
ChatRoomActivity
?)
?.
setupToolbarTitle
(
getString
(
R
.
string
.
title_members
,
totalMembers
)
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt
View file @
5e5e565d
...
...
@@ -2,13 +2,13 @@ package chat.rocket.android.pinnedmessages.presentation
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.
Get
ChatRoomsInteractor
import
chat.rocket.android.server.domain.ChatRoomsInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.rest.get
Room
PinnedMessages
import
chat.rocket.core.internal.rest.getPinnedMessages
import
chat.rocket.core.model.isSystemMessage
import
timber.log.Timber
import
javax.inject.Inject
...
...
@@ -17,7 +17,7 @@ class PinnedMessagesPresenter @Inject constructor(
private
val
view
:
PinnedMessagesView
,
private
val
strategy
:
CancelStrategy
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
roomsInteractor
:
Get
ChatRoomsInteractor
,
private
val
roomsInteractor
:
ChatRoomsInteractor
,
private
val
mapper
:
ViewModelMapper
,
factory
:
RocketChatClientFactory
)
{
...
...
@@ -37,7 +37,7 @@ class PinnedMessagesPresenter @Inject constructor(
chatRoom
?.
let
{
room
->
view
.
showLoading
()
val
pinnedMessages
=
client
.
get
Room
PinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
client
.
getPinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
pinnedMessagesListOffset
=
pinnedMessages
.
offset
.
toInt
()
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
.
filterNot
{
it
.
isSystemMessage
()
})
view
.
showPinnedMessages
(
messageList
)
...
...
app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt
View file @
5e5e565d
...
...
@@ -2,13 +2,13 @@ package chat.rocket.android.pinnedmessages.ui
import
android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.widget.DefaultItemAnimator
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
androidx.core.view.isVisible
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
...
...
@@ -17,14 +17,13 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter
import
chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.ui
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_pinned_messages.*
import
javax.inject.Inject
fun
newInstance
(
chatRoomId
:
String
,
chatRoomType
:
String
)
:
Fragment
{
fun
newInstance
(
chatRoomId
:
String
,
chatRoomType
:
String
):
Fragment
{
return
PinnedMessagesFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
putString
(
BUNDLE_CHAT_ROOM_ID
,
chatRoomId
)
...
...
@@ -49,15 +48,19 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
AndroidSupportInjection
.
inject
(
this
)
val
bundle
=
arguments
if
(
bundle
!=
null
){
if
(
bundle
!=
null
)
{
chatRoomId
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_ID
)
chatRoomType
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_TYPE
)
}
else
{
}
else
{
requireNotNull
(
bundle
)
{
"no arguments supplied when the fragment was instantiated"
}
}
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
=
container
?.
inflate
(
R
.
layout
.
fragment_pinned_messages
)
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
):
View
?
=
container
?.
inflate
(
R
.
layout
.
fragment_pinned_messages
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
...
...
@@ -69,21 +72,27 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
override
fun
showPinnedMessages
(
pinnedMessages
:
List
<
BaseViewModel
<*
>>)
{
ui
{
if
(
recycler_view_pinned
.
adapter
==
null
){
adapter
=
ChatRoomAdapter
(
chatRoomType
,
""
,
null
,
false
)
if
(
recycler_view_pinned
.
adapter
==
null
)
{
adapter
=
ChatRoomAdapter
(
chatRoomType
,
""
,
null
,
false
)
recycler_view_pinned
.
adapter
=
adapter
val
linearLayoutManager
=
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
val
linearLayoutManager
=
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
recycler_view_pinned
.
layoutManager
=
linearLayoutManager
recycler_view_pinned
.
itemAnimator
=
DefaultItemAnimator
()
if
(
pinnedMessages
.
size
>
10
){
recycler_view_pinned
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
){
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?)
{
if
(
pinnedMessages
.
size
>
10
)
{
recycler_view_pinned
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?
)
{
presenter
.
loadPinnedMessages
(
chatRoomId
)
}
})
}
togglePinView
(
pinnedMessages
.
size
)
pin_view
.
isVisible
=
pinnedMessages
.
isEmpty
(
)
}
adapter
.
appendData
(
pinnedMessages
)
}
...
...
@@ -104,22 +113,14 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showLoading
()
{
ui
{
view_loading
.
setVisible
(
true
)
}
ui
{
view_loading
.
isVisible
=
true
}
}
override
fun
hideLoading
()
{
ui
{
view_loading
.
setVisible
(
false
)
}
ui
{
view_loading
.
isVisible
=
false
}
}
private
fun
setupToolbar
()
{
(
activity
as
ChatRoomActivity
).
setupToolbarTitle
(
getString
(
R
.
string
.
title_pinned_messages
))
}
private
fun
togglePinView
(
size
:
Int
){
if
(
size
==
0
){
pin_view
.
setVisible
(
true
)
}
else
{
pin_view
.
setVisible
(
false
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
5e5e565d
...
...
@@ -291,7 +291,7 @@ class PushManager @Inject constructor(
.
setLabel
(
replyTextHint
)
.
build
()
val
pendingIntent
=
getReplyPendingIntent
(
pushMessage
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_
reply_black_24px
,
replyTextHint
,
pendingIntent
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_
action_message_reply_24dp
,
replyTextHint
,
pendingIntent
)
.
addRemoteInput
(
replyRemoteInput
)
.
setAllowGeneratedReplies
(
true
)
.
build
()
...
...
app/src/main/java/chat/rocket/android/server/domain/
Get
ChatRoomsInteractor.kt
→
app/src/main/java/chat/rocket/android/server/domain/ChatRoomsInteractor.kt
View file @
5e5e565d
...
...
@@ -5,7 +5,7 @@ import kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.withContext
import
javax.inject.Inject
class
Get
ChatRoomsInteractor
@Inject
constructor
(
private
val
repository
:
ChatRoomsRepository
)
{
class
ChatRoomsInteractor
@Inject
constructor
(
private
val
repository
:
ChatRoomsRepository
)
{
/**
* Get all [ChatRoom].
...
...
@@ -41,8 +41,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
* @return The [ChatRoom] object or null if we couldn't find any.
*/
suspend
fun
getById
(
serverUrl
:
String
,
roomId
:
String
):
ChatRoom
?
=
withContext
(
CommonPool
)
{
val
allChatRooms
=
repository
.
get
(
serverUrl
)
return
@withContext
allChatRooms
.
first
{
return
@withContext
repository
.
get
(
serverUrl
).
find
{
it
.
id
==
roomId
}
}
...
...
@@ -55,7 +54,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
* @return The [ChatRoom] object or null if we couldn't find any.
*/
fun
getByName
(
serverUrl
:
String
,
name
:
String
):
ChatRoom
?
{
return
getAll
(
serverUrl
).
toMutableList
().
find
{
chatRoom
->
chatRoom
.
n
ame
==
name
}
return
getAll
(
serverUrl
).
firstOrNull
{
it
.
name
==
name
||
it
.
fullN
ame
==
name
}
}
/**
...
...
app/src/main/java/chat/rocket/android/server/domain/GetPermissionsInteractor.kt
deleted
100644 → 0
View file @
b0817ace
package
chat.rocket.android.server.domain
import
javax.inject.Inject
class
GetPermissionsInteractor
@Inject
constructor
(
private
val
settingsRepository
:
SettingsRepository
,
private
val
currentServerRepository
:
CurrentServerRepository
)
{
private
fun
publicSettings
():
PublicSettings
?
=
settingsRepository
.
get
(
currentServerRepository
.
get
()
!!
)
/**
* Check whether user is allowed to delete a message.
*/
fun
allowedMessageDeleting
()
=
publicSettings
()
?.
allowedMessageDeleting
()
?:
false
/**
* Checks whether user is allowed to edit a message.
*/
fun
allowedMessageEditing
()
=
publicSettings
()
?.
allowedMessageEditing
()
?:
false
/**
* Checks whether user is allowed to pin a message to a channel.
*/
fun
allowedMessagePinning
()
=
publicSettings
()
?.
allowedMessagePinning
()
?:
false
/**
* Checks whether should show deleted message status.
*/
fun
showDeletedStatus
()
=
publicSettings
()
?.
showDeletedStatus
()
?:
false
/**
* Checks whether should show edited message status.
*/
fun
showEditedStatus
()
=
publicSettings
()
?.
showEditedStatus
()
?:
false
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/domain/PermissionsInteractor.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.server.domain
import
chat.rocket.android.helper.UserHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.core.model.Permission
import
javax.inject.Inject
// Creating rooms
const
val
CREATE_PUBLIC_CHANNELS
=
"create-c"
const
val
CREATE_DIRECT_MESSAGES
=
"create-d"
const
val
CREATE_PRIVATE_CHANNELS
=
"create-p"
// Messages
const
val
DELETE_MESSAGE
=
"delete-message"
const
val
FORCE_DELETE_MESSAGE
=
"force-delete-message"
const
val
EDIT_MESSAGE
=
"edit-message"
const
val
PIN_MESSAGE
=
"pin-message"
const
val
POST_READONLY
=
"post-readonly"
class
PermissionsInteractor
@Inject
constructor
(
private
val
settingsRepository
:
SettingsRepository
,
private
val
permissionsRepository
:
PermissionsRepository
,
private
val
getCurrentServerInteractor
:
GetCurrentServerInteractor
,
private
val
userHelper
:
UserHelper
)
{
private
fun
publicSettings
():
PublicSettings
?
=
settingsRepository
.
get
(
currentServerUrl
()
!!
)
fun
saveAll
(
permissions
:
List
<
Permission
>)
{
val
url
=
currentServerUrl
()
!!
permissions
.
forEach
{
permissionsRepository
.
save
(
url
,
it
)
}
}
/**
* Check whether the user is allowed to delete a message.
*/
fun
allowedMessageDeleting
()
=
publicSettings
()
?.
allowedMessageDeleting
()
?:
false
/**
* Checks whether the user is allowed to edit a message.
*/
fun
allowedMessageEditing
()
=
publicSettings
()
?.
allowedMessageEditing
()
?:
false
/**
* Checks whether the user is allowed to pin a message to a channel.
*/
fun
allowedMessagePinning
()
=
publicSettings
()
?.
allowedMessagePinning
()
?:
false
/**
* Checks whether the user is allowed to star a message.
*/
fun
allowedMessageStarring
()
=
publicSettings
()
?.
allowedMessageStarring
()
?:
false
/**
* Checks whether should show deleted message status.
*/
fun
showDeletedStatus
()
=
publicSettings
()
?.
showDeletedStatus
()
?:
false
/**
* Checks whether should show edited message status.
*/
fun
showEditedStatus
()
=
publicSettings
()
?.
showEditedStatus
()
?:
false
fun
canPostToReadOnlyChannels
():
Boolean
{
val
url
=
getCurrentServerInteractor
.
get
()
!!
val
currentUserRoles
=
userHelper
.
user
()
?.
roles
return
permissionsRepository
.
get
(
url
,
POST_READONLY
)
?.
let
{
permission
->
currentUserRoles
?.
isNotEmpty
()
==
true
&&
permission
.
roles
.
any
{
currentUserRoles
.
contains
(
it
)
}
}
==
true
||
userHelper
.
isAdmin
()
}
private
fun
currentServerUrl
():
String
?
{
return
getCurrentServerInteractor
.
get
()
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/domain/PermissionsRepository.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.server.domain
import
chat.rocket.core.model.Permission
interface
PermissionsRepository
{
/**
* Store [permission] locally.
*
* @param url The server url from where we're interest to store the permission.
* @param permission The permission to store.
*/
fun
save
(
url
:
String
,
permission
:
Permission
)
/**
* Get permission given by the [permissionId] and for the server [url].
*
* @param url The server url from where we're interested on getting the permissions.
* @param permissionId the id of the permission to get.
*
* @return The interested [Permission] or null if not found.
*/
fun
get
(
url
:
String
,
permissionId
:
String
):
Permission
?
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/domain/RefreshSettingsInteractor.kt
View file @
5e5e565d
...
...
@@ -24,7 +24,7 @@ class RefreshSettingsInteractor @Inject constructor(
FAVORITE_ROOMS
,
UPLOAD_STORAGE_TYPE
,
UPLOAD_MAX_FILE_SIZE
,
UPLOAD_WHITELIST_MIMETYPES
,
HIDE_USER_JOIN
,
HIDE_USER_LEAVE
,
HIDE_TYPE_AU
,
HIDE_MUTE_UNMUTE
,
HIDE_TYPE_RU
,
ALLOW_MESSAGE_DELETING
,
ALLOW_MESSAGE_EDITING
,
ALLOW_MESSAGE_PINNING
,
SHOW_DELETED_STATUS
,
SHOW_EDITED_STATUS
,
ALLOW_MESSAGE_EDITING
,
ALLOW_MESSAGE_PINNING
,
ALLOW_MESSAGE_STARRING
,
SHOW_DELETED_STATUS
,
SHOW_EDITED_STATUS
,
WIDE_TILE_310
,
STORE_LAST_MESSAGE
)
suspend
fun
refresh
(
server
:
String
)
{
...
...
app/src/main/java/chat/rocket/android/server/domain/SaveActiveUsersInteractor.kt
View file @
5e5e565d
...
...
@@ -29,7 +29,8 @@ class SaveActiveUsersInteractor @Inject constructor(
username
=
user
.
username
?:
it
.
username
,
status
=
user
.
status
?:
it
.
status
,
emails
=
user
.
emails
?:
it
.
emails
,
utcOffset
=
user
.
utcOffset
?:
it
.
utcOffset
utcOffset
=
user
.
utcOffset
?:
it
.
utcOffset
,
roles
=
user
.
roles
?:
it
.
roles
)
val
activeUserList
:
MutableList
<
User
>
=
...
...
app/src/main/java/chat/rocket/android/server/domain/SettingsRepository.kt
View file @
5e5e565d
...
...
@@ -49,6 +49,7 @@ const val ALLOW_MESSAGE_EDITING = "Message_AllowEditing"
const
val
SHOW_DELETED_STATUS
=
"Message_ShowDeletedStatus"
const
val
SHOW_EDITED_STATUS
=
"Message_ShowEditedStatus"
const
val
ALLOW_MESSAGE_PINNING
=
"Message_AllowPinning"
const
val
ALLOW_MESSAGE_STARRING
=
"Message_AllowStarring"
const
val
STORE_LAST_MESSAGE
=
"Store_Last_Message"
/*
...
...
@@ -83,6 +84,7 @@ fun PublicSettings.wideTile(): String? = this[WIDE_TILE_310]?.value as String?
fun
PublicSettings
.
showDeletedStatus
():
Boolean
=
this
[
SHOW_DELETED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
showEditedStatus
():
Boolean
=
this
[
SHOW_EDITED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
allowedMessagePinning
():
Boolean
=
this
[
ALLOW_MESSAGE_PINNING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageStarring
():
Boolean
=
this
[
ALLOW_MESSAGE_STARRING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageEditing
():
Boolean
=
this
[
ALLOW_MESSAGE_EDITING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageDeleting
():
Boolean
=
this
[
ALLOW_MESSAGE_DELETING
]
?.
value
==
true
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
View file @
5e5e565d
...
...
@@ -110,7 +110,6 @@ class ConnectionManager(internal val client: RocketChatClient) {
Timber
.
d
(
"Received new Message for room ${message.roomId}"
)
val
channel
=
roomMessagesChannels
[
message
.
roomId
]
channel
?.
send
(
message
)
}
}
...
...
@@ -131,6 +130,7 @@ class ConnectionManager(internal val client: RocketChatClient) {
}
}
}
client
.
connect
()
// Broadcast initial state...
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/SharedPreferencesPermissionsRepository.kt
0 → 100644
View file @
5e5e565d
package
chat.rocket.android.server.infraestructure
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.PermissionsRepository
import
chat.rocket.core.model.Permission
import
com.squareup.moshi.Moshi
class
SharedPreferencesPermissionsRepository
(
private
val
localRepository
:
LocalRepository
,
moshi
:
Moshi
)
:
PermissionsRepository
{
private
val
adapter
=
moshi
.
adapter
(
Permission
::
class
.
java
)
override
fun
save
(
url
:
String
,
permission
:
Permission
)
{
localRepository
.
save
(
getPermissionKey
(
url
,
permission
.
id
),
adapter
.
toJson
(
permission
))
}
override
fun
get
(
url
:
String
,
permissionId
:
String
):
Permission
?
{
return
localRepository
.
get
(
getPermissionKey
(
url
,
permissionId
))
?.
let
{
adapter
.
fromJson
(
it
)
}
}
// Create a key following the pattern: settings_[url]_[permission id]
// eg.: 'settings_https://open.rocket.chat_create-p'
private
fun
getPermissionKey
(
url
:
String
,
permissionId
:
String
):
String
{
return
"${LocalRepository.PERMISSIONS_KEY}${url}_$permissionId"
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/SharedPreferencesSettingsRepository.kt
View file @
5e5e565d
...
...
@@ -6,7 +6,9 @@ import chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.core.internal.SettingsAdapter
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
private
val
adapter
=
SettingsAdapter
().
lenient
()
...
...
app/src/main/java/chat/rocket/android/util/extensions/Rx.kt
View file @
5e5e565d
...
...
@@ -6,9 +6,8 @@ import io.reactivex.Observable
import
io.reactivex.android.schedulers.AndroidSchedulers
import
java.util.concurrent.TimeUnit
fun
EditText
.
asObservable
(
debounceTimeout
:
Long
=
100
):
Observable
<
CharSequence
>
{
fun
EditText
.
asObservable
():
Observable
<
CharSequence
>
{
return
RxTextView
.
textChanges
(
this
)
.
debounce
(
debounceTimeout
,
TimeUnit
.
MILLISECONDS
)
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribeOn
(
AndroidSchedulers
.
mainThread
())
}
\ No newline at end of file
app/src/main/res/drawable/ic_
content_copy_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_copy_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z
"
android:
fillColor=
"@color/actionMenuColor"
/>
android:
fillColor=
"@color/actionMenuColor
"
android:
pathData=
"M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_
delete_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_delete_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z
"
android:
fillColor=
"#FF0000"
/>
android:
fillColor=
"#FF0000
"
android:
pathData=
"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
/>
</vector>
app/src/main/res/drawable/ic_
edit_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_edit_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z
"
android:
fillColor=
"@color/actionMenuColor"
/>
android:
fillColor=
"@color/actionMenuColor
"
android:
pathData=
"M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_action_message_pin_24dp.xml
0 → 100644
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportHeight=
"24"
android:viewportWidth=
"24"
>
<path
android:fillColor=
"@color/actionMenuColor"
android:pathData=
"M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_
quote_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_quote_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M6,17h3l2,-4L11,7L5,7v6h3zM14,17h3l2,-4L19,7h-6v6h3z
"
android:
fillColor=
"@color/actionMenuColor"
/>
android:
fillColor=
"@color/actionMenuColor
"
android:
pathData=
"M6,17h3l2,-4L11,7L5,7v6h3zM14,17h3l2,-4L19,7h-6v6h3z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_
reply_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_reply_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z
"
android:
fillColor=
"@color/actionMenuColor"
/>
android:
fillColor=
"@color/actionMenuColor
"
android:
pathData=
"M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_
star_black_24px
.xml
→
app/src/main/res/drawable/ic_
action_message_star_24dp
.xml
View file @
5e5e565d
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:
pathData=
"M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z
"
android:
fillColor=
"@color/actionMenuColor"
/>
android:
fillColor=
"@color/actionMenuColor
"
android:
pathData=
"M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_arrow_back_white_24dp.xml
View file @
5e5e565d
<vector
android:autoMirrored=
"true"
android:height=
"24dp"
android:tint=
"#FFFFFF"
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
android:width=
"24dp"
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<path
android:fillColor=
"#FF000000"
android:pathData=
"M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"
/>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:tint=
"#FFFFFF"
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
<path
android:fillColor=
"#FF000000"
android:pathData=
"M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"
/>
</vector>
app/src/main/res/drawable/ic_hashtag_black.xml
deleted
100644 → 0
View file @
b0817ace
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"13dp"
android:height=
"16dp"
android:viewportWidth=
"13.0"
android:viewportHeight=
"16.0"
>
<path
android:pathData=
"M6.626,11.495L4.505,11.495L3.714,16L1.703,16L2.495,11.495L0,11.495L0,9.604L2.824,9.604L3.374,6.484L0.824,6.484L0.824,4.571L3.714,4.571L4.516,0L6.516,0L5.714,4.571L7.846,4.571L8.648,0L10.659,0L9.857,4.571L12.264,4.571L12.264,6.484L9.516,6.484L8.967,9.604L11.429,9.604L11.429,11.495L8.637,11.495L7.846,16L5.835,16L6.626,11.495ZM4.835,9.604L6.956,9.604L7.505,6.484L5.374,6.484L4.835,9.604Z"
android:strokeColor=
"#00000000"
android:fillType=
"evenOdd"
android:fillColor=
"#000000"
android:strokeWidth=
"1"
/>
</vector>
app/src/main/res/drawable/ic_pin_black_24dp.xml
deleted
100644 → 0
View file @
b0817ace
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:viewportWidth=
"197.218"
android:viewportHeight=
"197.218"
android:width=
"197.218dp"
android:height=
"197.218dp"
>
<group
android:translateX=
"-570.396"
android:translateY=
"-306.782"
>
<path
android:pathData=
"M704.445 306.782l-6.785 6.785c-6.084 6.084 -7.622 14.712 -4.309 21.871l-44.068 35.44 -3.086 -3.086c-7.889 -7.889 -19.525 -7.889 -27.414 0l-8.944 8.953 87.821 87.811 8.934 -8.933c7.899 -7.899 7.899 -19.525 0 -27.433l-3.076 -3.077 36.051 -44.68c6.824 2.466 14.367 1.036 20.037 -4.624l8.008 -5.858 -63.169 -63.169zm-66.867 116.487l-67.182 66.857 0 13.874 13.864 0 66.867 -67.182 -13.549 -13.549z"
android:fillColor=
"@color/actionMenuColor"
/>
</group>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_room_channel.xml
View file @
5e5e565d
app/src/main/res/drawable/ic_room_dm.xml
View file @
5e5e565d
app/src/main/res/drawable/ic_room_lock.xml
View file @
5e5e565d
app/src/main/res/drawable/message_reply_button_bg.xml
0 → 100644
View file @
5e5e565d
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<solid
android:color=
"@android:color/white"
/>
<corners
android:radius=
"4dp"
/>
<stroke
android:color=
"#1D74F5"
android:width=
"2dp"
/>
</shape>
\ No newline at end of file
app/src/main/res/layout/activity_pinned_messages.xml
deleted
100644 → 0
View file @
b0817ace
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:theme=
"@style/AppTheme"
tools:context=
".chatroom.ui.PinnedMessagesActivity"
>
<include
android:id=
"@+id/layout_app_bar"
layout=
"@layout/app_bar_chat_room"
/>
<FrameLayout
android:id=
"@+id/fragment_container"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_below=
"@+id/layout_app_bar"
/>
</RelativeLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_chat_room.xml
View file @
5e5e565d
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/root_layout"
...
...
@@ -12,23 +11,23 @@
android:id=
"@+id/view_loading"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"parent"
android:visibility=
"gone"
app:indicatorColor=
"@color/black"
app:indicatorName=
"BallPulseIndicator"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:visibility=
"visible"
/>
<FrameLayout
android:id=
"@+id/message_list_container"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
app:layout_constraint
Start_toStartOf=
"parent
"
app:layout_constraint
Bottom_toTopOf=
"@id/text_typing_status
"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraint
Top_toTop
Of=
"parent"
app:layout_constraint
Bottom_toTopOf=
"@id/layout_message_composer
"
>
app:layout_constraint
Start_toStart
Of=
"parent"
app:layout_constraint
Top_toTopOf=
"parent
"
>
<include
android:id=
"@+id/layout_message_list"
...
...
@@ -44,52 +43,65 @@
android:layout_height=
"100dp"
android:src=
"@drawable/ic_chat_black_24dp"
android:tint=
"@color/icon_grey"
app:layout_constraintStart_toStartOf=
"parent"
android:visibility=
"gone"
app:layout_constraintBottom_toTopOf=
"@id/text_chat_title"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintBottom_toTopOf=
"@id/text_chat_title"
app:layout_constraintVertical_chainStyle=
"packed"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/text_chat_title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"24dp"
android:text=
"@string/msg_no_chat_title"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/image_chat_icon"
app:layout_constraintBottom_toTopOf=
"@id/text_chat_description"
android:textColor=
"@color/colorSecondaryText"
android:textSize=
"20sp"
android:layout_marginTop=
"24dp"
android:textStyle=
"bold"
android:textColor=
"@color/colorSecondaryText"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
app:layout_constraintBottom_toTopOf=
"@id/text_chat_description"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/image_chat_icon"
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/text_chat_description"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"@string/msg_no_chat_description"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/text_chat_title"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
android:layout_marginTop=
"16dp"
android:text=
"@string/msg_no_chat_description"
android:textAlignment=
"center"
android:textSize=
"16sp"
android:textColor=
"@color/colorSecondaryTextLight"
android:textSize=
"16sp"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/text_chat_title"
tools:visibility=
"visible"
/>
<chat.rocket.android.widget.autocompletion.ui.SuggestionsView
android:id=
"@+id/suggestions_view"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"@color/suggestion_background_color"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
/>
<TextView
android:id=
"@+id/text_typing_status"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"5dp"
android:layout_marginEnd=
"16dp"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"16dp"
android:maxLines=
"2"
android:visibility=
"gone"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
a
ndroid:background=
"@color/suggestion_background_color
"
/>
a
pp:layout_constraintEnd_toStartOf=
"parent
"
/>
<include
android:id=
"@+id/layout_message_composer"
...
...
@@ -102,18 +114,18 @@
android:id=
"@+id/view_dim"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
android:background=
"@color/colorDim"
android:visibility=
"gone"
/>
android:visibility=
"gone"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
/>
<include
android:id=
"@+id/layout_message_attachment_options"
layout=
"@layout/message_attachment_options"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
android:layout_margin=
"5dp"
android:visibility=
"gone"
/>
android:visibility=
"gone"
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
/>
<TextView
android:id=
"@+id/connection_status_text"
...
...
app/src/main/res/layout/fragment_chat_rooms.xml
View file @
5e5e565d
...
...
@@ -45,4 +45,15 @@
tools:text=
"connected"
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/text_no_search"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"56dp"
android:text=
"@string/msg_no_search_found"
android:textSize=
"20sp"
android:layout_centerHorizontal=
"true"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
</RelativeLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_favorite_messages.xml
0 → 100644
View file @
5e5e565d
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
tools:context=
".favoritemessages.ui.FavoriteMessagesFragment"
>
<android.support.v7.widget.RecyclerView
android:id=
"@+id/recycler_view"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:scrollbars=
"vertical"
/>
<com.wang.avi.AVLoadingIndicatorView
android:id=
"@+id/view_loading"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_centerInParent=
"true"
android:visibility=
"gone"
app:indicatorColor=
"@color/black"
app:indicatorName=
"BallPulseIndicator"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:visibility=
"visible"
/>
<ImageView
android:id=
"@+id/image_star"
android:layout_width=
"100dp"
android:layout_height=
"100dp"
android:src=
"@drawable/ic_action_message_star_24dp"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toTopOf=
"@+id/text_no_favorite_messages"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_chainStyle=
"packed"
/>
<TextView
android:id=
"@+id/text_no_favorite_messages"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"24dp"
android:text=
"@string/no_favorite_messages"
android:textColor=
"@color/colorSecondaryText"
android:textSize=
"20sp"
android:textStyle=
"bold"
app:layout_constraintBottom_toTopOf=
"@+id/text_no_favorite_messages_description"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/image_star"
/>
<TextView
android:id=
"@+id/text_no_favorite_messages_description"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"16dp"
android:text=
"@string/no_favorite_description"
android:textAlignment=
"center"
android:textColor=
"@color/colorSecondaryTextLight"
android:textSize=
"16sp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_no_favorite_messages"
/>
<android.support.constraint.Group
android:id=
"@+id/no_messages_view"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:visibility=
"gone"
app:constraint_referenced_ids=
"text_no_favorite_messages_description,image_star,text_no_favorite_messages"
tools:visibility=
"visible"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_pinned_messages.xml
View file @
5e5e565d
...
...
@@ -3,18 +3,14 @@
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
android:layout_height=
"match_parent"
tools:context=
".pinnedmessages.ui.PinnedMessagesFragment"
>
<android.support.v7.widget.RecyclerView
android:id=
"@+id/recycler_view_pinned"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.0"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"1.0"
/>
android:scrollbars=
"vertical"
/>
<com.wang.avi.AVLoadingIndicatorView
android:id=
"@+id/view_loading"
...
...
@@ -36,48 +32,48 @@
android:id=
"@+id/iv_pin_icon"
android:layout_width=
"100dp"
android:layout_height=
"100dp"
android:src=
"@drawable/ic_
pin_black
_24dp"
android:src=
"@drawable/ic_
action_message_pin
_24dp"
android:tint=
"@color/icon_grey"
app:layout_constraint
Start_toStartOf=
"parent
"
app:layout_constraint
Bottom_toTopOf=
"@id/tv_pin_title
"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintBottom_toTopOf=
"@id/tv_pin_title"
app:layout_constraintVertical_chainStyle=
"packed"
/>
<TextView
android:id=
"@+id/tv_pin_title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"24dp"
android:text=
"@string/no_pinned_messages"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/iv_pin_icon"
app:layout_constraintBottom_toTopOf=
"@id/tv_pin_description"
android:textColor=
"@color/colorSecondaryText"
android:textSize=
"20sp"
android:layout_marginTop=
"24dp"
android:textStyle=
"bold"
android:textColor=
"@color/colorSecondaryText"
/>
app:layout_constraintBottom_toTopOf=
"@id/tv_pin_description"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/iv_pin_icon"
/>
<TextView
android:id=
"@+id/tv_pin_description"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"@string/no_pinned_description"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/tv_pin_title"
app:layout_constraintBottom_toBottomOf=
"parent"
android:layout_marginTop=
"16dp"
android:text=
"@string/no_pinned_description"
android:textAlignment=
"center"
android:textColor=
"@color/colorSecondaryTextLight"
android:textSize=
"16sp"
android:textColor=
"@color/colorSecondaryTextLight"
/>
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/tv_pin_title"
/>
<android.support.constraint.Group
android:id=
"@+id/pin_view"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:constraint_referenced_ids=
"tv_pin_description,iv_pin_icon,tv_pin_title"
android:visibility=
"gone"
app:constraint_referenced_ids=
"tv_pin_description,iv_pin_icon,tv_pin_title"
tools:visibility=
"visible"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/item_message.xml
View file @
5e5e565d
...
...
@@ -7,11 +7,11 @@
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:paddingStart=
"@dimen/screen_edge_left_and_right_padding"
android:paddingEnd=
"@dimen/screen_edge_left_and_right_padding"
android:paddingTop=
"@dimen/message_item_top_and_bottom_padding"
android:focusable=
"true"
android:paddingBottom=
"@dimen/message_item_top_and_bottom_padding"
android:focusable=
"true"
>
android:paddingEnd=
"@dimen/screen_edge_left_and_right_padding"
android:paddingStart=
"@dimen/screen_edge_left_and_right_padding"
android:paddingTop=
"@dimen/message_item_top_and_bottom_padding"
>
<include
android:id=
"@+id/layout_avatar"
...
...
@@ -20,36 +20,39 @@
android:layout_height=
"40dp"
android:layout_marginTop=
"5dp"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@id/new_messages_notif"
/>
app:layout_constraintTop_toBottomOf=
"@
+
id/new_messages_notif"
/>
<LinearLayout
android:id=
"@+id/new_messages_notif"
tools:visibility=
"visible"
android:visibility=
"gone"
android:orientation=
"horizontal"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"horizontal"
android:visibility=
"gone"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
app:layout_constraintTop_toTopOf=
"parent"
tools:visibility=
"visible"
>
<View
android:layout_gravity=
"center"
android:layout_height=
"1dp"
android:layout_width=
"0dp"
android:layout_weight=
"1"
android:layout_height=
"1dp"
android:layout_gravity=
"center"
android:layout_marginEnd=
"4dp"
android:background=
"@color/red"
/>
android:layout_weight=
"1"
android:background=
"@color/red"
/>
<TextView
android:layout_width=
"wrap_content"
android:text=
"@string/msg_unread_messages"
android:layout_height=
"wrap_content"
android:text=
"@string/msg_unread_messages"
android:textColor=
"@color/red"
/>
<View
android:layout_gravity=
"center"
android:layout_height=
"1dp"
android:layout_width=
"0dp"
android:layout_weight=
"1"
android:layout_height=
"1dp"
android:layout_gravity=
"center"
android:layout_marginStart=
"4dp"
android:background=
"@color/red"
/>
android:layout_weight=
"1"
android:background=
"@color/red"
/>
</LinearLayout>
<LinearLayout
...
...
@@ -58,8 +61,8 @@
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:orientation=
"horizontal"
app:layout_constraint
Top_toBottomOf=
"@id/new_messages_notif
"
app:layout_constraint
Left_toRightOf=
"@+id/layout_avatar
"
>
app:layout_constraint
Left_toRightOf=
"@+id/layout_avatar
"
app:layout_constraint
Top_toBottomOf=
"@+id/new_messages_notif
"
>
<TextView
android:id=
"@+id/text_sender"
...
...
@@ -75,6 +78,27 @@
android:layout_height=
"wrap_content"
android:layout_marginStart=
"10dp"
tools:text=
"11:45 PM"
/>
<TextView
android:id=
"@+id/text_edit_indicator"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:text=
"@string/msg_edited"
android:textStyle=
"italic"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
<ImageView
android:id=
"@+id/image_star_indicator"
android:layout_width=
"14dp"
android:layout_height=
"14dp"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"8dp"
android:layout_marginTop=
"2dp"
android:src=
"@drawable/ic_action_message_star_24dp"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
</LinearLayout>
<TextView
...
...
@@ -84,12 +108,13 @@
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"2dp"
android:layout_marginTop=
"5dp"
app:layout_constraintLeft_toLeftOf=
"@id/top_container"
app:layout_constraintLeft_toLeftOf=
"@
+
id/top_container"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/top_container"
tools:text=
"This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!"
/>
<include
layout=
"@layout/layout_reactions"
<include
layout=
"@layout/layout_reactions"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintEnd_toEndOf=
"@+id/text_content"
...
...
app/src/main/res/layout/item_message_reply.xml
0 → 100644
View file @
5e5e565d
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/message_container"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:paddingBottom=
"@dimen/message_item_top_and_bottom_padding"
android:paddingEnd=
"@dimen/screen_edge_left_and_right_padding"
android:paddingStart=
"@dimen/screen_edge_left_and_right_padding"
android:paddingTop=
"@dimen/message_item_top_and_bottom_padding"
>
<Button
android:id=
"@+id/button_message_reply"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"2dp"
android:layout_marginTop=
"5dp"
android:layout_marginStart=
"56dp"
android:background=
"@drawable/message_reply_button_bg"
android:text=
"@string/action_msg_reply"
android:textAllCaps=
"false"
android:textColor=
"#1D74F5"
android:textSize=
"14sp"
app:layout_constraintLeft_toLeftOf=
"parent"
/>
<include
layout=
"@layout/layout_reactions"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintEnd_toEndOf=
"@+id/button_message_reply"
app:layout_constraintStart_toStartOf=
"@+id/button_message_reply"
app:layout_constraintTop_toBottomOf=
"@+id/button_message_reply"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/menu/chatroom_actions.xml
View file @
5e5e565d
...
...
@@ -11,4 +11,9 @@
android:id=
"@+id/action_pinned_messages"
android:title=
"@string/title_pinned_messages"
app:showAsAction=
"never"
/>
<item
android:id=
"@+id/action_favorite_messages"
android:title=
"@string/title_favorite_messages"
app:showAsAction=
"never"
/>
</menu>
\ No newline at end of file
app/src/main/res/menu/message_actions.xml
View file @
5e5e565d
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<group
android:id=
"@+id/common_actions"
>
<item
android:id=
"@+id/action_menu_msg
_reply"
android:icon=
"@drawable/ic_reply_black_24px
"
android:id=
"@+id/action_message
_reply"
android:icon=
"@drawable/ic_action_message_reply_24dp
"
android:title=
"@string/action_msg_reply"
/>
<item
android:id=
"@+id/action_menu_msg
_quote"
android:icon=
"@drawable/ic_quote_black_24px
"
android:id=
"@+id/action_message
_quote"
android:icon=
"@drawable/ic_action_message_quote_24dp
"
android:title=
"@string/action_msg_quote"
/>
<item
android:id=
"@+id/action_menu_msg_edit"
android:icon=
"@drawable/ic_edit_black_24px"
android:title=
"@string/action_msg_edit"
/>
<item
android:id=
"@+id/action_menu_msg_copy"
android:icon=
"@drawable/ic_content_copy_black_24px"
android:id=
"@+id/action_message_copy"
android:icon=
"@drawable/ic_action_message_copy_24dp"
android:title=
"@string/action_msg_copy"
/>
<item
...
...
@@ -33,20 +27,22 @@
<!--android:title="@string/action_msg_share" />-->
<item
android:id=
"@+id/action_menu_msg_pin_unpin"
android:icon=
"@drawable/ic_pin_black_24dp"
android:id=
"@+id/action_message_star"
android:icon=
"@drawable/ic_action_message_star_24dp"
android:title=
"@string/action_msg_star"
/>
<item
android:id=
"@+id/action_message_unpin"
android:icon=
"@drawable/ic_action_message_pin_24dp"
android:title=
"@string/action_msg_pin"
/>
<!--<item-->
<!--android:id="@+id/action_menu_msg_star"-->
<!--andrtextIconicon="@drawable/ic_star_black_24px"-->
<!--android:title="@string/action_msg_star" />-->
</group>
<item
android:id=
"@+id/action_message_edit"
android:icon=
"@drawable/ic_action_message_edit_24dp"
android:title=
"@string/action_msg_edit"
/>
<group
android:id=
"@+id/dangerous_actions"
>
<item
android:id=
"@+id/action_menu_msg
_delete"
android:icon=
"@drawable/ic_delete_black_24px
"
android:id=
"@+id/action_message
_delete"
android:icon=
"@drawable/ic_action_message_delete_24dp
"
android:title=
"@string/action_msg_delete"
/>
</group>
</menu>
\ No newline at end of file
app/src/main/res/values-es/strings.xml
View file @
5e5e565d
...
...
@@ -100,7 +100,6 @@
<string
name=
"msg_ver_not_minimum"
>
Parece que la versión del servidor está por debajo de la versión mínima requerida %1$s.\nActualice su servidor para iniciar sesión!
</string>
<string
name=
"msg_proceed"
>
PROCEDER
</string>
<string
name=
"msg_cancel"
>
CANCELAR
</string>
<string
name=
"msg_warning"
>
ADVERTENCIA
</string>
...
...
@@ -111,6 +110,16 @@
<string
name=
"msg_image_saved_failed"
>
Error al guardar la imagen
</string>
<string
name=
"msg_no_chat_title"
>
Sin mensajes de chat
</string>
<string
name=
"msg_no_chat_description"
>
Comience a conversar para ver\nsus mensajes aquí.
</string>
<string
name=
"msg_edited"
>
(editado)
</string>
// TODO: Add proper translation.
<string
name=
"msg_and"
>
\u0020and\u0020
</string>
// TODO: Add proper translation.
<string
name=
"msg_is_typing"
>
\u0020is typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_are_typing"
>
\u0020are typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_several_users_are_typing"
>
Several users are typing…
</string>
<string
name=
"msg_no_search_found"
>
No se han encontrado resultados
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Nombre de la sala cambiado para: %1$s por %2$s
</string>
...
...
@@ -121,6 +130,10 @@
<string
name=
"message_welcome"
>
Bienvenido %s
</string>
<string
name=
"message_removed"
>
Mensaje eliminado
</string>
<string
name=
"message_pinned"
>
Fijado una mensaje:
</string>
<string
name=
"message_muted"
>
Usuario %1$s silenciado por %2$s
</string>
<string
name=
"message_unmuted"
>
Usuario %1$s no silenciado por %2$s
</string>
<string
name=
"message_role_add"
>
%1$s fue establecido %2$s por %3$s
</string>
<string
name=
"message_role_removed"
>
%1$s ya no es %2$s por %3$s
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
Respuesta
</string>
...
...
@@ -131,6 +144,8 @@
<string
name=
"action_msg_pin"
>
Fijar mensaje
</string>
<string
name=
"action_msg_unpin"
>
Soltar mensaje
</string>
<string
name=
"action_msg_star"
>
Star mensaje
</string>
// TODO: Add proper translation.
<string
name=
"action_msg_unstar"
>
Unstar Message
</string>
<string
name=
"action_msg_share"
>
Compartir
</string>
<string
name=
"action_title_editing"
>
Edición de mensaje
</string>
<string
name=
"action_msg_add_reaction"
>
Añadir una reacción
</string>
...
...
@@ -139,6 +154,8 @@
<string
name=
"permission_editing_not_allowed"
>
La edición no és permitida
</string>
<string
name=
"permission_deleting_not_allowed"
>
Eliminar no és permitido
</string>
<string
name=
"permission_pinning_not_allowed"
>
Fijar no és permitido
</string>
// TODO: Add proper translation.
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Lista de miembros
</string>
...
...
@@ -146,7 +163,13 @@
<!-- Pinned Messages -->
<string
name=
"title_pinned_messages"
>
Mensajes fijados
</string>
<string
name=
"no_pinned_messages"
>
Sin mensajes fijadas
</string>
<string
name=
"no_pinned_description"
>
Todas las mensajes fijadas\naparecen aquí.
</string>
<string
name=
"no_pinned_description"
>
Todas las mensajes fijadas\naparecen aquí
</string>
<!-- Favorite Messages -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_messages"
>
Favorite Messages
</string>
<string
name=
"no_favorite_messages"
>
No favorite messages
</string>
<string
name=
"no_favorite_description"
>
All the favorite messages\nappear here
</string>
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
Tamaño del archivo (%1$d bytes) excedió el tamaño máximo de carga de %2$d bytes
</string>
...
...
app/src/main/res/values-fr/strings.xml
View file @
5e5e565d
...
...
@@ -110,6 +110,16 @@
<string
name=
"msg_image_saved_failed"
>
Échec de l\'enregistrement de l\'image
</string>
<string
name=
"msg_no_chat_title"
>
Aucun message de discussion
</string>
<string
name=
"msg_no_chat_description"
>
Commencez à converser pour voir\nvos messages ici.
</string>
<string
name=
"msg_edited"
>
(édité)
</string>
// TODO: Add proper translation.
<string
name=
"msg_and"
>
\u0020and\u0020
</string>
// TODO: Add proper translation.
<string
name=
"msg_is_typing"
>
\u0020is typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_are_typing"
>
\u0020are typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_several_users_are_typing"
>
Several users are typing…
</string>
<string
name=
"msg_no_search_found"
>
Aucun résultat trouvé
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Le nom de le salle a changé à: %1$s par %2$s
</string>
...
...
@@ -120,6 +130,10 @@
<string
name=
"message_welcome"
>
Bienvenue %s
</string>
<string
name=
"message_removed"
>
Message supprimé
</string>
<string
name=
"message_pinned"
>
Épinglé un message:
</string>
<string
name=
"message_muted"
>
Utilisateur %1$s mis en sourdine par %2$s
</string>
<string
name=
"message_unmuted"
>
Utilisateur %1$s non muté par %2$s
</string>
<string
name=
"message_role_add"
>
%1$s a été défini %2$s par %3$s
</string>
<string
name=
"message_role_removed"
>
%1$s is no longer %2$s par %3$s
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
Répondre
</string>
...
...
@@ -129,7 +143,10 @@
<string
name=
"action_msg_delete"
>
Effacer
</string>
<string
name=
"action_msg_pin"
>
Épingle message
</string>
<string
name=
"action_msg_unpin"
>
Enlever message
</string>
// TODO: Add proper translation.
<string
name=
"action_msg_star"
>
Star message
</string>
// TODO: Add proper translation.
<string
name=
"action_msg_unstar"
>
Unstar Message
</string>
<string
name=
"action_msg_share"
>
Partager
</string>
<string
name=
"action_title_editing"
>
Modification du message
</string>
<string
name=
"action_msg_add_reaction"
>
Ajouter une réaction
</string>
...
...
@@ -138,6 +155,8 @@
<string
name=
"permission_editing_not_allowed"
>
L\'édition n\'est pas autorisée
</string>
<string
name=
"permission_deleting_not_allowed"
>
La suppression n\'est pas autorisée
</string>
<string
name=
"permission_pinning_not_allowed"
>
L\'épinglage n\'est pas autorisé
</string>
// TODO: Add proper translation.
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Liste des membres
</string>
...
...
@@ -145,7 +164,13 @@
<!-- Pinned Messages -->
<string
name=
"title_pinned_messages"
>
Messages épinglés
</string>
<string
name=
"no_pinned_messages"
>
Aucun message épinglé
</string>
<string
name=
"no_pinned_description"
>
Tous les messages épinglés\napparaissent ici.
</string>
<string
name=
"no_pinned_description"
>
Tous les messages épinglés\napparaissent ici
</string>
<!-- Favorite Messages -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_messages"
>
Favorite Messages
</string>
<string
name=
"no_favorite_messages"
>
No favorite messages
</string>
<string
name=
"no_favorite_description"
>
All the favorite messages\nappear here
</string>
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
Taille du fichier (%1$d bytes) dépassé la taille de téléchargement maximale de %2$d bytes
</string>
...
...
app/src/main/res/values-hi-rIN/strings.xml
View file @
5e5e565d
...
...
@@ -112,6 +112,16 @@
<string
name=
"msg_image_saved_failed"
>
छवि को सहेजने में विफल
</string>
<string
name=
"msg_no_chat_title"
>
कोई चैट संदेश नहीं
</string>
<string
name=
"msg_no_chat_description"
>
यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।
</string>
<string
name=
"msg_edited"
>
(संपादित)
</string>
// TODO: Add proper translation.
<string
name=
"msg_and"
>
\u0020and\u0020
</string>
// TODO: Add proper translation.
<string
name=
"msg_is_typing"
>
\u0020is typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_are_typing"
>
\u0020are typing…
</string>
// TODO: Add proper translation.
<string
name=
"msg_several_users_are_typing"
>
Several users are typing…
</string>
<string
name=
"msg_no_search_found"
>
कोई परिणाम नहीं मिला
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
%2$s ने रूम का नाम बदलकर %1$s किया
</string>
...
...
@@ -122,6 +132,10 @@
<string
name=
"message_welcome"
>
%s का स्वागत करते हैं
</string>
<string
name=
"message_removed"
>
संदेश हटाया गया
</string>
<string
name=
"message_pinned"
>
एक संदेश पिन किया:
</string>
<string
name=
"message_muted"
>
उपयोगकर्ता %1$s %2$s द्वारा म्यूट किया गया
</string>
<string
name=
"message_unmuted"
>
उपयोगकर्ता %1$s %2$s द्वारा अनम्यूट किया गया
</string>
<string
name=
"message_role_add"
>
%1$s %3$s द्वारा %2$s सेट किया गया था
</string>
<string
name=
"message_role_removed"
>
%1$s अब %3$s द्वारा %2$s नहीं है
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
जवाब दें
</string>
...
...
@@ -132,6 +146,8 @@
<string
name=
"action_msg_pin"
>
संदेश को पिन करें
</string>
<string
name=
"action_msg_unpin"
>
संदेश को पिन से हटाएँ
</string>
<string
name=
"action_msg_star"
>
संदेश को स्टार करें
</string>
// TODO: Add proper translation.
<string
name=
"action_msg_unstar"
>
Unstar Message
</string>
<string
name=
"action_msg_share"
>
शेयर करें
</string>
<string
name=
"action_title_editing"
>
संपादन संदेश
</string>
<string
name=
"action_msg_add_reaction"
>
प्रतिक्रिया जोड़ें
</string>
...
...
@@ -140,6 +156,8 @@
<string
name=
"permission_editing_not_allowed"
>
संपादन की अनुमति नहीं है
</string>
<string
name=
"permission_deleting_not_allowed"
>
हटाने की अनुमति नहीं है
</string>
<string
name=
"permission_pinning_not_allowed"
>
पिनि करने की अनुमति नहीं है
</string>
// TODO: Add proper translation.
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
सदस्यों की सूची
</string>
...
...
@@ -149,6 +167,12 @@
<string
name=
"no_pinned_messages"
>
कोई पिन संदेश नहीं
</string>
<string
name=
"no_pinned_description"
>
सभी पिन किए गए संदेश यहां\nदिखाई देते हैं।
</string>
<!-- Favorite Messages -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_messages"
>
Favorite Messages
</string>
<string
name=
"no_favorite_messages"
>
No favorite messages
</string>
<string
name=
"no_favorite_description"
>
All the favorite messages\nappear here
</string>
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
फ़ाइल का आकार %1$d बाइट्स ने %2$d बाइट्स के अधिकतम अपलोड आकार को पार कर लिया है
</string>
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
5e5e565d
...
...
@@ -106,16 +106,26 @@
<string
name=
"msg_invalid_server_protocol"
>
O protocolo selecionado não é suportado pelo servidor, por favor utilize HTTPS e tente novamente
</string>
<string
name=
"msg_image_saved_successfully"
>
Imagem salva na galeria
</string>
<string
name=
"msg_image_saved_failed"
>
Falha ao salvar a imagem
</string>
<string
name=
"msg_edited"
>
(editado)
</string>
<string
name=
"msg_and"
>
\u0020e\u0020
</string>
<string
name=
"msg_is_typing"
>
\u0020está digitando…
</string>
<string
name=
"msg_are_typing"
>
\u0020estão digitando…
</string>
<string
name=
"msg_several_users_are_typing"
>
Vários usuários estão digitando…
</string>
<string
name=
"msg_no_search_found"
>
nenhum resultado encontrado
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Nome da sala alterado para: %1$s por %2$s
</string>
<string
name=
"message_user_added_by"
>
Usuário %1$s adicionado por %2$s
</string>
<string
name=
"message_user_removed_by"
>
Usuário %1$s removido por %2$s
</string>
<string
name=
"message_user_left"
>
Saiu da sala
.
</string>
<string
name=
"message_user_joined_channel"
>
Entrou na sala
.
</string>
<string
name=
"message_user_left"
>
Saiu da sala
</string>
<string
name=
"message_user_joined_channel"
>
Entrou na sala
</string>
<string
name=
"message_welcome"
>
Bem-vindo, %s
</string>
<string
name=
"message_removed"
>
Mensagem removida
</string>
<string
name=
"message_pinned"
>
Pinou uma mensagem:
</string>
<string
name=
"message_muted"
>
Usuário %1$s entrou no modo mudo por %2$s
</string>
<string
name=
"message_unmuted"
>
Usuário %1$s saiu do modo mudo por %2$s
</string>
<string
name=
"message_role_add"
>
%1$s foi definido %2$s por %3$s
</string>
<string
name=
"message_role_removed"
>
%1$s não é mais %2$s por %3$s
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
Responder
</string>
...
...
@@ -123,17 +133,19 @@
<string
name=
"action_msg_copy"
>
Copiar
</string>
<string
name=
"action_msg_quote"
>
Citar
</string>
<string
name=
"action_msg_delete"
>
Remover
</string>
<string
name=
"action_msg_pin"
>
Fixar Mensagem
</string>
<string
name=
"action_msg_unpin"
>
Desafixar Mensagem
</string>
<string
name=
"action_msg_star"
>
Favoritar Mensagem
</string>
<string
name=
"action_msg_pin"
>
Pinar mensagem
</string>
<string
name=
"action_msg_unpin"
>
Despinar mensagem
</string>
<string
name=
"action_msg_star"
>
Favoritar mensagem
</string>
<string
name=
"action_msg_unstar"
>
Desfavoritar messagem
</string>
<string
name=
"action_msg_share"
>
Compartilhar
</string>
<string
name=
"action_title_editing"
>
Editando
M
ensagem
</string>
<string
name=
"action_title_editing"
>
Editando
m
ensagem
</string>
<string
name=
"action_msg_add_reaction"
>
Adicionar reação
</string>
<!-- Permission messages -->
<string
name=
"permission_editing_not_allowed"
>
Edição não permitida
</string>
<string
name=
"permission_deleting_not_allowed"
>
Remoção não permitida
</string>
<string
name=
"permission_pinning_not_allowed"
>
Pinagem não permitida
</string>
<string
name=
"permission_starring_not_allowed"
>
Favoritar não permitido
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Lista de Membros
</string>
...
...
@@ -143,12 +155,18 @@
<string
name=
"no_pinned_messages"
>
Nenhuma mensagem pinada
</string>
<string
name=
"no_pinned_description"
>
Todas as mensagens pinadas\naparecerão aqui
</string>
<!-- Favorite Messages -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_messages"
>
Messagens Favoritas
</string>
<string
name=
"no_favorite_messages"
>
Nenhuma messagem favorita
</string>
<string
name=
"no_favorite_description"
>
Todas as mensagens favoritas\naparecerão aqui
</string>
<!-- Upload Messages -->
<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_disconnected"
>
Descone
c
tado
</string>
<string
name=
"status_connecting"
>
Conectando
</string>
<string
name=
"status_authenticating"
>
Autenticando
</string>
<string
name=
"status_disconnecting"
>
Desconectando
</string>
...
...
@@ -165,7 +183,7 @@
<string
name=
"Slash_Tableflip_Description"
>
Exibir (╯°□°)╯︵ ┻━┻
</string>
<string
name=
"Slash_TableUnflip_Description"
>
Exibir ┬─┬ ノ( ゜-゜ノ)
</string>
<string
name=
"Create_A_New_Channel"
>
Criar um novo canal
</string>
<string
name=
"Show_the_keyboard_shortcut_list"
>
Show the keyboard shortcut list
</string>
<string
name=
"Show_the_keyboard_shortcut_list"
>
Exibir a lista de atalhos do teclado
</string>
<string
name=
"Invite_user_to_join_channel_all_from"
>
do [#canal] para entrar neste
</string>
<string
name=
"Invite_user_to_join_channel_all_to"
>
Convidar todos os usuários deste canal para entrar no [#canal]
</string>
<string
name=
"Archive"
>
Arquivar
</string>
...
...
@@ -173,8 +191,8 @@
<string
name=
"Leave_the_current_channel"
>
Sair do canal atual
</string>
<string
name=
"Displays_action_text"
>
Exibir texto de ação
</string>
<string
name=
"Direct_message_someone"
>
Enviar DM para alguém
</string>
<string
name=
"Mute_someone_in_room"
>
Silenciar
alguém
</string>
<string
name=
"Unmute_someone_in_room"
>
De
-silenciar
alguém na sala
</string>
<string
name=
"Mute_someone_in_room"
>
Ativar o modo mudo em
alguém
</string>
<string
name=
"Unmute_someone_in_room"
>
De
sativar o modo mudo em
alguém na sala
</string>
<string
name=
"Invite_user_to_join_channel"
>
Convidar algum usuário para entrar neste canal
</string>
<string
name=
"Unarchive"
>
Desarquivar
</string>
<string
name=
"Join_the_given_channel"
>
Entrar no canal especificado
</string>
...
...
app/src/main/res/values/strings.xml
View file @
5e5e565d
...
...
@@ -107,6 +107,12 @@
<string
name=
"msg_invalid_server_protocol"
>
The selected protocol is not accepted by this server, try using HTTPS
</string>
<string
name=
"msg_image_saved_successfully"
>
Image has been saved to gallery
</string>
<string
name=
"msg_image_saved_failed"
>
Failed to save image
</string>
<string
name=
"msg_edited"
>
(edited)
</string>
<string
name=
"msg_and"
>
\u0020and\u0020
</string>
<string
name=
"msg_is_typing"
>
\u0020is typing…
</string>
<string
name=
"msg_are_typing"
>
\u0020are typing…
</string>
<string
name=
"msg_several_users_are_typing"
>
Several users are typing…
</string>
<string
name=
"msg_no_search_found"
>
No result found
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Room name changed to: %1$s by %2$s
</string>
...
...
@@ -117,6 +123,10 @@
<string
name=
"message_welcome"
>
Welcome %s
</string>
<string
name=
"message_removed"
>
Message removed
</string>
<string
name=
"message_pinned"
>
Pinned a message:
</string>
<string
name=
"message_muted"
>
User %1$s muted by %2$s
</string>
<string
name=
"message_unmuted"
>
User %1$s unmuted by %2$s
</string>
<string
name=
"message_role_add"
>
%1$s was set %2$s by %3$s
</string>
<string
name=
"message_role_removed"
>
%1$s is no longer %2$s by %3$s
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
Reply
</string>
...
...
@@ -127,6 +137,7 @@
<string
name=
"action_msg_pin"
>
Pin Message
</string>
<string
name=
"action_msg_unpin"
>
Unpin Message
</string>
<string
name=
"action_msg_star"
>
Star Message
</string>
<string
name=
"action_msg_unstar"
>
Unstar Message
</string>
<string
name=
"action_msg_share"
>
Share
</string>
<string
name=
"action_title_editing"
>
Editing Message
</string>
<string
name=
"action_msg_add_reaction"
>
Add reaction
</string>
...
...
@@ -135,6 +146,7 @@
<string
name=
"permission_editing_not_allowed"
>
Editing is not allowed
</string>
<string
name=
"permission_deleting_not_allowed"
>
Deleting is not allowed
</string>
<string
name=
"permission_pinning_not_allowed"
>
Pinning is not allowed
</string>
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Members List
</string>
...
...
@@ -142,7 +154,12 @@
<!-- Pinned Messages -->
<string
name=
"title_pinned_messages"
>
Pinned Messages
</string>
<string
name=
"no_pinned_messages"
>
No pinned messages
</string>
<string
name=
"no_pinned_description"
>
All the pinned messages\nappear here.
</string>
<string
name=
"no_pinned_description"
>
All the pinned messages\nappear here
</string>
<!-- Favorite Messages -->
<string
name=
"title_favorite_messages"
>
Favorite Messages
</string>
<string
name=
"no_favorite_messages"
>
No favorite messages
</string>
<string
name=
"no_favorite_description"
>
All the favorite messages\nappear here
</string>
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
File size %1$d bytes exceeded max upload size of %2$d bytes
</string>
...
...
app/src/test/java/chat/rocket/android/MemoryMessagesRepositoryTest.kt
deleted
100644 → 0
View file @
b0817ace
package
chat.rocket.android
import
chat.rocket.android.server.infraestructure.MemoryMessagesRepository
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.MessageType
import
kotlinx.coroutines.experimental.runBlocking
import
org.hamcrest.CoreMatchers.notNullValue
import
org.hamcrest.MatcherAssert.assertThat
import
org.junit.Before
import
org.junit.Test
import
org.hamcrest.CoreMatchers.`is`
as
isEqualTo
class
MemoryMessagesRepositoryTest
{
val
repository
=
MemoryMessagesRepository
()
val
msg
=
Message
(
id
=
"messageId"
,
roomId
=
"GENERAL"
,
message
=
"Beam me up, Scotty."
,
timestamp
=
1511443964815
,
attachments
=
null
,
sender
=
null
,
avatar
=
null
,
channels
=
null
,
editedAt
=
null
,
editedBy
=
null
,
groupable
=
true
,
mentions
=
null
,
parseUrls
=
false
,
senderAlias
=
null
,
type
=
MessageType
.
MessageRemoved
(),
updatedAt
=
1511443964815
,
urls
=
null
,
pinned
=
false
,
reactions
=
null
)
val
msg2
=
Message
(
id
=
"messageId2"
,
roomId
=
"sandbox"
,
message
=
"Highly Illogical"
,
timestamp
=
1511443964818
,
attachments
=
null
,
sender
=
null
,
avatar
=
null
,
channels
=
null
,
editedAt
=
null
,
editedBy
=
null
,
groupable
=
true
,
mentions
=
null
,
parseUrls
=
false
,
senderAlias
=
null
,
type
=
MessageType
.
MessageRemoved
(),
updatedAt
=
1511443964818
,
urls
=
null
,
pinned
=
false
,
reactions
=
null
)
@Before
fun
setup
()
{
runBlocking
{
repository
.
clear
()
}
}
@Test
fun
`
save
()
should
save
a
single
message
`
()
{
runBlocking
{
assertThat
(
repository
.
getAll
().
size
,
isEqualTo
(
0
))
repository
.
save
(
msg
)
val
allMessages
=
repository
.
getAll
()
assertThat
(
allMessages
.
size
,
isEqualTo
(
1
))
allMessages
[
0
].
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId"
))
assertThat
(
message
,
isEqualTo
(
"Beam me up, Scotty."
))
assertThat
(
roomId
,
isEqualTo
(
"GENERAL"
))
}
}
}
@Test
fun
`
saveAll
()
should
all
saved
messages
`
()
{
runBlocking
{
assertThat
(
repository
.
getAll
().
size
,
isEqualTo
(
0
))
repository
.
saveAll
(
listOf
(
msg
,
msg2
))
val
allMessages
=
repository
.
getAll
()
assertThat
(
allMessages
.
size
,
isEqualTo
(
2
))
allMessages
[
0
].
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId"
))
assertThat
(
message
,
isEqualTo
(
"Beam me up, Scotty."
))
assertThat
(
roomId
,
isEqualTo
(
"GENERAL"
))
}
allMessages
[
1
].
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId2"
))
assertThat
(
message
,
isEqualTo
(
"Highly Illogical"
))
assertThat
(
roomId
,
isEqualTo
(
"sandbox"
))
}
}
}
@Test
fun
`
getById
()
should
return
a
single
message
`
()
{
runBlocking
{
repository
.
saveAll
(
listOf
(
msg
,
msg2
))
var
singleMsg
=
repository
.
getById
(
"messageId"
)
assertThat
(
singleMsg
,
notNullValue
())
singleMsg
!!
.
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId"
))
assertThat
(
message
,
isEqualTo
(
"Beam me up, Scotty."
))
assertThat
(
roomId
,
isEqualTo
(
"GENERAL"
))
}
singleMsg
=
repository
.
getById
(
"messageId2"
)
assertThat
(
singleMsg
,
notNullValue
())
singleMsg
!!
.
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId2"
))
assertThat
(
message
,
isEqualTo
(
"Highly Illogical"
))
assertThat
(
roomId
,
isEqualTo
(
"sandbox"
))
}
}
}
@Test
fun
`
getByRoomId
()
should
return
all
messages
for
room
id
or
an
empty
list
`
()
{
runBlocking
{
repository
.
saveAll
(
listOf
(
msg
,
msg2
))
var
roomMessages
=
repository
.
getByRoomId
(
"faAad32fkasods2"
)
assertThat
(
roomMessages
.
isEmpty
(),
isEqualTo
(
true
))
roomMessages
=
repository
.
getByRoomId
(
"sandbox"
)
assertThat
(
roomMessages
.
size
,
isEqualTo
(
1
))
roomMessages
[
0
].
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId2"
))
assertThat
(
message
,
isEqualTo
(
"Highly Illogical"
))
assertThat
(
roomId
,
isEqualTo
(
"sandbox"
))
}
roomMessages
=
repository
.
getByRoomId
(
"GENERAL"
)
assertThat
(
roomMessages
.
size
,
isEqualTo
(
1
))
roomMessages
[
0
].
apply
{
assertThat
(
id
,
isEqualTo
(
"messageId"
))
assertThat
(
message
,
isEqualTo
(
"Beam me up, Scotty."
))
assertThat
(
roomId
,
isEqualTo
(
"GENERAL"
))
}
}
}
}
\ No newline at end of file
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